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().
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:
class Location:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
Example instance:
forest = Location("Ye olde forest")
print(forest.name) # Works: prints 'Ye olde forest'
But this fails:
current_location = "forest"
print(current_location.name) # Error: 'str' object has no attribute 'name'
I want reusable code for any location, e.g.:
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)
- python getattr — access instance attributes by string
- python getattr self: using getattr inside methods and setattr patterns
- Practical pattern for text-adventure games: a Game API and examples
- Performance, safety and design trade-offs (getattr setattr python)
- Sources
- Conclusion
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 doobj = globals().get(current_location_name)orimport 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:
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:
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:
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
setattrto 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:
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:
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:
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:
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.
getattrhas 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. Usingglobals()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
locationsdict or aGameregistry documents your game state and makes serialization, saving/loading, and unit testing straightforward. -
Behavior differences:
getattrrespects properties and__getattr__fallbacks; direct__dict__access (vars()) does not. If your Location uses properties or descriptors, usegetattrto ensure correct behavior. -
When to use
__getattribute__? Rarely. It controls all attribute access and can introduce subtle bugs; prefer__getattr__orgetattrfor 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
- Python | getattr() method — GeeksforGeeks
- Python’s getattr function — Python Morsels
- getattr() — Codecademy docs
- Using Python’s .dict to Work With Attributes — Real Python
- How to Change a Dictionary Into a Class? — GeeksforGeeks
- Python Dynamic Attribute Access Using Object Methods — SQLPey
- Python getattr() Function Explained — Towards Data Science
- dict: where Python stores attributes — Python Morsels
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.