I propose extending Python’s built-in getattr function to support nested attribute access using dotted strings. This feature would simplify retrieving deeply nested attributes dynamically without the need for custom implementations or looping. For example:
Current workaround
value = getattr(getattr(getattr(getattr(myobj, 'employee'), 'department'), 'manager'), 'name')
# or with a custom implementation
def deep_getattr(obj, attr):
for key in attr.split('.'):
obj = getattr(obj, key)
return obj
value = deep_getattr(myobj, 'employee.department.manager.name')
Proposed built-in support
value = getattr(myobj, 'employee.department.manager.name')
Benefits:
Reduces boilerplate code for nested attribute traversal.
Aligns with Python’s philosophy of simplicity and readability.
Useful for dynamic object traversal in frameworks, APIs, and data-driven applications.
Questions to Discuss:
Should this be a built-in feature, or remain a helper function?
Should the feature also handle indexed access for lists (e.g., user.children[0].first_name)? (imo, no need)
Discussion Question:
Would this feature simplify enough common use cases to justify adding it to the standard library, or is it too niche for inclusion?
This does already exist in a slightly different form - operator.attrgetter('employee.department.manager.name')(obj), though probably you’d store the getter object. Putting this in getattr() would be a bad idea though - getattr/setattr are explicitly documented as allowing any string for attributes, including dotted names. Objects can store under any name, it’s just that .attr syntax limits what names are possible there. So adding this would be backwards incompatible.
You usually write myobj.employee.department.manager.name instead of getattr(getattr(getattr(getattr(myobj, 'employee'), 'department'), 'manager'), 'name').
To give the original poster the benefit of the doubt, I can only presume that the example they showed was for demonstration. The use case for getattr is generally to have a dynamic attribute name, or to be used with the default to avoid ugly try statements.
Thank you for bringing this up! If modifying getattr were still on the table, an optional keyword argument (e.g., nested=True) could distinguish between regular and dotted-name access, preserving compatibility. For example:
value = getattr(obj, "abc.xyz") # Existing behavior
value = getattr(obj, "abc.xyz", nested=True) # New one
This was just the idea I was going to give if some would say this.
But I now guess this proposal is not the right fit for Python’s design principles or practical implementation.
True, but this targets dynamic access when the path is a runtime string like "employee.department.manager.name". Tools like operator.attrgetter help, and I appreciate the feedback!
In case of dynamic access you would need first to create a dot-separated string from dynamic names, and then getattr would need to split it on separate attribute names. This is not particularly efficient.
Got the point! Thanks for the feedback, will appreciate that!
I’ll try to research more on my next feature req and look for more advanced change suggestions.
I ran into a case where this would be helpful again today, but for a reason I don’t think anybody brought up yet — the default argument.
Needing to access a specific nested attribute in structured data, where somewhere down the line, some of the attributes might start to be missing, because they’re only available under certain scenarios, the best implementation is using a try ... except block. I find this a bit overly verbose and unnecessary.
In my case, I was already inside a try ... except block, which is what finally prompted me to propose a change / join a proposal discussion.
From a technical point of view, I think implementing nested access in getattr is a bit problematic, because, like it or not, even though they cannot be accessed via normal syntax, attributes can have dots in their name. On top of that, operator.attrgetter already exists, which implements nested access already, and isn’t such a critical component of the language.
I don’t really see any reason why operator.attrgetter, and for consistency operator.itemgetter, couldn’t get a default keyword argument, bringing it on-par with getattr’s functionality.
The main technical consideration here, I think, are which exceptions should they suppress. For operator.attrgetter, I think is pretty straight-forward — only AttributeError. For operator.itemgetter, I think it’s a fair a bit more common to possibly to encounter exceptions other than KeyError, but I think only suppressing KeyError is a very reasonable implementation choice.