Welcome, guest | Sign In | My Account | Store | Cart

getattr allows one to obtain an attribute from an object given it's name as a string. multi_getattr does the same thing but with dotted strings that can represent nested attributes.

Python, 26 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def multi_getattr(obj, attr, default = None):
    """
    Get a named attribute from an object; multi_getattr(x, 'a.b.c.d') is
    equivalent to x.a.b.c.d. When a default argument is given, it is
    returned when any attribute in the chain doesn't exist; without
    it, an exception is raised when a missing attribute is encountered.

    """
    attributes = attr.split(".")
    for i in attributes:
        try:
            obj = getattr(obj, i)
        except AttributeError:
            if default:
                return default
            else:
                raise
    return obj
        
# Example usage
obj  = [1,2,3]
attr = "append.__doc__.capitalize.__doc__"

multi_getattr(obj, attr) #Will return the docstring for the
                         #capitalize method of the builtin string
                         #object

This first became useful to me when I needed to make some alterations to http://djangosnippets.org/snippets/660/. It seemed useful enough to generalise a little.

7 comments

Danny W. Adair 13 years, 8 months ago  # | flag

Thank you, I would suggest two improvements: a) allow callables b) allow default values which have a truth value of False (e.g. None, 0, won't work with the recipe). Untested:

def multi_getattr(obj, attr, **kw):
    attributes = attr.split(".")
    for i in attributes:
        try:
            obj = getattr(obj, i)
            if callable(obj):
                obj = obj()
        except AttributeError:
            if kw.has_key('default'):
                return kw['default']
            else:
                raise
    return obj
Danny W. Adair 13 years, 8 months ago  # | flag

P.S.: a) If you had existing code doing

x = multi_getattr(myobj, 'some.attr.path', 'my default value')

you could go with args instead (or in addition to) *kw

b) Also nice as a mixin class! :-)

Noufal Ibrahim (author) 13 years, 8 months ago  # | flag

Thanks Danny. I'm not sure about the callable thing though. It just seems like one of the many things that a given attribute could be "*able". Also, calling something just because it's callable might lead to TypeErrors in case of invalid number of arguments. I'd leave the calling to the user rather than to the function. The point about the defaults is very valid though. Thanks!

I'm not sure I understand your second comment about the mixin classes. Can you elaborate?

Sunjay Varma 13 years, 8 months ago  # | flag

Great recipe. :)

Stefan Behnel 13 years, 8 months ago  # | flag

Note that operator.attrgetter supports this out of the box in Python 2.6 and later.

Noufal Ibrahim (author) 13 years, 8 months ago  # | flag

Ah. Thanks Stefan. I needed this for a 2.5 python and that too inside a Django template tag where the environment is quite limited.

Chaobin Tang (ε”θΆ…ζ–Œ) 13 years, 8 months ago  # | flag

hi N -

The recipe is rather simple, but really pythonic and practical, i liked it.

But when I reproduced this one in my own project, I changed the function name to get_nested_attr.

Thanks,

Created by Noufal Ibrahim on Thu, 5 Aug 2010 (MIT)
Python recipes (4591)
Noufal Ibrahim's recipes (10)

Required Modules

  • (none specified)

Other Information and Tasks