Programming

Python getattr: Dynamic Access to Class Attributes by String

Learn how to use Python getattr to dynamically access class instance attributes with a string variable. Ideal for text adventure games mapping names like 'forest' to objects. Covers dict mapping, getattr(self), setattr patterns, and safe alternatives to globals() or eval().

1 answer 1 view

How to dynamically access Python class instance attributes using a string variable containing the instance name?

I’m building a text adventure game like Zork and using classes for locations:

python
class Location:
 def __init__(self, name):
 self.name = name

 def __str__(self):
 return self.name

Example instance:

python
forest = Location("Ye olde forest")
print(forest.name) # Works: prints 'Ye olde forest'

But this fails:

python
current_location = "forest"
print(current_location.name) # Error: 'str' object has no attribute 'name'

I want reusable code for any location, e.g.:

python
current_location = "forest"
if current_location.light == 0:
 # Handle dark location (need lit lamp, etc.)

With dicts, it’s locations['forest']['light'], but I prefer classes for cleaner access like current_location.light. How can I map a string variable to the actual instance and access its properties dynamically? Should I use a dictionary of instances, globals(), or getattr()?

Use a simple mapping from names to instances (a dict) and then access attributes with getattr — e.g., loc = locations[current_name]; getattr(loc, 'light', 0). Inside methods you can use python getattr self style calls like getattr(self, 'attrname', default), and you should avoid eval() or blind use of globals() for production code. If you want the convenience of current_location.light, keep the current-location name internally (string) but expose a property that returns the instance for that name.


Contents


Mapping strategies: dict vs globals() vs getattr (python getattr vs getattribute)

You have three realistic ways to map the string “forest” to the Location instance named forest:

  • A dedicated dictionary mapping names to instances (recommended).
    Example: locations = {'forest': forest, 'cave': cave}. Lookup is explicit, fast and safe: loc = locations.get(current_location_name).

  • Module/global namespace lookup (not recommended for production).
    You can do obj = globals().get(current_location_name) or import sys; obj = getattr(sys.modules[__name__], current_location_name, None). This works because variables at module scope live in that namespace, but it couples logic to variable names and is brittle.

  • Evaluate names dynamically (avoid). eval(current_location) will return the object if a variable with that name exists, but it’s insecure and can run arbitrary code.

Which should you pick? Use the dictionary or a dedicated registry (e.g., game.locations). Want a one-line way to resolve a name? loc = locations.get(name) is clear and explicit. If you must search the module namespace for quick debugging, getattr(sys.modules[__name__], name, None) is an option — but validate first.

For background on dynamic access functions like getattr, see python getattr explanations and examples at GeeksforGeeks and Python Morsels: https://www.geeksforgeeks.org/python/python-getattr-method/ and https://www.pythonmorsels.com/getattr-function/.


python getattr — access instance attributes by string

Once you have the instance, use getattr to read attributes whose names you only have as strings:

  • Syntax: getattr(object, name[, default]).
  • Example:
python
loc = locations.get('forest')
light = getattr(loc, 'light', 0) # returns 0 if 'light' doesn't exist

That’s safer than loc.__dict__['light'] because getattr respects descriptors, properties, and __getattr__ fallbacks. If you prefer the raw namespace you can use vars(loc).get('light') or loc.__dict__, but that bypasses properties and can break encapsulation; see Real Python on __dict__ and vars() for details: https://realpython.com/python-dict-attribute/.

Want to both read and write dynamically? Use setattr(object, 'attrname', value). Example:

python
setattr(loc, 'light', 1) # creates/updates attribute 'light'

For more usage examples, read the built-in docs and tutorials at Codecademy or GeeksforGeeks: https://www.codecademy.com/resources/docs/python/built-in-functions/getattr and https://www.geeksforgeeks.org/python/python-getattr-method/.

A small note on getattr vs __getattribute__: getattr(obj, name) triggers the normal attribute lookup flow — it calls obj.__getattribute__ and if that raises AttributeError Python will call obj.__getattr__ (if defined). Overriding __getattribute__ is advanced and changes every attribute access; use getattr unless you need that low-level control. See Towards Data Science for a practical explanation: https://towardsdatascience.com/python-getattr-function-explained-pyshark-cc7f49c59b2e/.


python getattr self: using getattr inside methods and setattr patterns

Inside a class method you can use getattr(self, ...) when attribute names are dynamic. Example:

python
class Location:
 def __init__(self, name, **attrs):
 self.name = name
 # set any extra attributes dynamically
 for k, v in attrs.items():
 setattr(self, k, v)

 def is_dark(self):
 # check attribute 'light' by name
 return getattr(self, 'light', 0) == 0

That pattern is handy when you load locations from JSON or a data file. To turn a dict into an object you can use the common for key, value in my_dict.items(): setattr(self, key, value) idiom — see the GeeksforGeeks example on converting a dictionary to instance attributes: https://www.geeksforgeeks.org/python/how-to-change-a-dictionary-into-a-class/.

Two short tips:

  • Use getattr(self, 'x', default) when the attribute may be missing.
  • Use setattr to populate instance attributes from external data (maps cleanly to serialization/deserialization).

Practical pattern for text-adventure games: a Game API and examples

Here’s a small, practical architecture that keeps your code readable and lets you write game.current_location.light or if game.current_location and game.current_location.light == 0:.

Code example:

python
class Location:
 def __init__(self, name, light=1, description=''):
 self.name = name
 self.light = light
 self.description = description
 def __str__(self):
 return self.name

class Game:
 def __init__(self):
 self.locations = {} # name -> Location
 self._current_location_name = None

 def add_location(self, name, location):
 self.locations[name] = location

 def set_current(self, name):
 if name not in self.locations:
 raise KeyError(f"No location named {name!r}")
 self._current_location_name = name

 @property
 def current_location(self):
 return self.locations.get(self._current_location_name)

Usage:

python
forest = Location("Ye olde forest", light=0)
game = Game()
game.add_location('forest', forest)
game.set_current('forest')

loc = game.current_location
if getattr(loc, 'light', 0) == 0:
 print("It's pitch dark here — you'll need a lamp.")

Why this pattern? You keep the canonical mapping in one place (game.locations). The rest of the code asks the game for the current location instance, so callers can use clean attribute access like game.current_location.light. If you must keep a top-level string variable (for a simple script), convert it to an instance at the moment of use:

python
current_location_name = 'forest'
loc = locations.get(current_location_name)
if loc and getattr(loc, 'light', 0) == 0:
 ...

If you ever need a registry built automatically (e.g., during prototyping) you can create it from globals for quick REPL checks:

python
locations = {name: obj for name, obj in globals().items() if isinstance(obj, Location)}

But do this only for quick-and-dirty scripts; a deliberate dict or registry is clearer and safer in a full project.


Performance, safety and design trade-offs (getattr setattr python)

  • Performance: dict lookups are O(1) and very fast. getattr has tiny overhead but is fine for game loops unless you’re calling it millions of times per tick. If you need extreme speed, cache the instance or attribute value where appropriate.

  • Safety: never use eval() on untrusted input. Using globals() or module lookups exposes you to name collisions and accidental access to unrelated objects. Prefer explicit registries and validate names (whitelists) if names come from users.

  • Maintainability: an explicit locations dict or a Game registry documents your game state and makes serialization, saving/loading, and unit testing straightforward.

  • Behavior differences: getattr respects properties and __getattr__ fallbacks; direct __dict__ access (vars()) does not. If your Location uses properties or descriptors, use getattr to ensure correct behavior.

  • When to use __getattribute__? Rarely. It controls all attribute access and can introduce subtle bugs; prefer __getattr__ or getattr for dynamic lookups unless you know what you’re doing.

For more examples comparing dynamic attribute access methods and when to use each, see SQLPey and Python Morsels: https://sqlpey.com/python/python-dynamic-attribute-access/ and https://www.pythonmorsels.com/getattr-function/.


Sources


Conclusion

Use an explicit mapping (a dict or a Game registry) to map strings like "forest" to Location instances, and use getattr(instance, 'light', default) to read attributes dynamically. Inside methods use getattr(self, ...) or setattr(self, ...) when attribute names are dynamic. Avoid eval() and be careful with globals(); a registry plus getattr gives clean, safe, and testable code for text-adventure state management.

Authors
Verified by moderation
Moderation
Python getattr: Dynamic Access to Class Attributes by String