Enhancing `getattr` to support nested attribute access with dotted strings

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:

  1. Should this be a built-in feature, or remain a helper function?
  2. 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?

1 Like

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.

13 Likes

You usually write myobj.employee.department.manager.name instead of getattr(getattr(getattr(getattr(myobj, 'employee'), 'department'), 'manager'), 'name').

6 Likes

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.

1 Like

Not that this is good practice, but this precise change isn’t workable, for the following reason of perfectly valid code:

class A: pass
z = A()
setattr(z, "abc.xyz", 1)
print(getattr(z, "abc.xyz")) # prints 1

I know a few libraries that allow for arbitrary fields with this for, e.g. setting properties on graph nodes or other things.

1 Like

Thank you for pointing that out! I hadn’t considered it fully in the context of this proposal.

1 Like

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.

2 Likes

Another way to implement this yourself:

import functools
def deep_getattr(obj, attr):
    return functools.reduce(getattr, attr.split('.'), obj)
2 Likes

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. :+1: