Search code examples
pythonpython-3.xgetattrgetattribute

use one function to getattr for either functions or regular attributes


I have the following code:

In [38]: %paste
def set_session_attribute(obj, attribute):
    if attribute.endswith('()'):
        attribute = attribute.replace('()', '')
        return getattr(obj, attribute)()
    else:
        return getattr(obj, attribute)


class Thing(object):
    def __init__(self):
        self.legs = 4
    def length(self):
        return 6

## -- End pasted text --

In [39]: x = Thing()

In [40]: y = set_session_attribute(x, 'legs')

In [41]: y
Out[41]: 4

In [42]: z = set_session_attribute(x, 'length()')

In [43]: z
Out[43]: 6

This is because calling with "length()" didn't work (AttributeError, no attribute length())

Is there a shorter, more maintainable way to make functions like this? Thank you.


Solution

  • Solution 1

    You can make length a property:

    class Thing(object):
        def __init__(self):
            self.legs = 4
        @property
        def length(self):
            return 6
    
    >>> thing = Thing()
    >>> thing.legs
    4
    >>> thing.length
    6
    

    If you really want to use your function:

    def set_session_attribute(obj, attribute):
        return getattr(obj, attribute)
    
    >>> set_session_attribute(thing, 'legs')
    4
    >>> set_session_attribute(thing, 'length')
    6
    

    If you cannot change the source of thing directly, you can do after importing the class:

    class Thing(object):
        def __init__(self):
            self.legs = 4
        def length(self):
            return 6
    

    Here:

    Thing.length2 = property(Thing.length)
    
    >>> thing = Thing()
    >>> thing.length2
    6
    

    Solution 2

    Alternatively, you can check if the attribute is callable:

    class Thing(object):
        def __init__(self):
            self.legs = 4
        def length(self):
            return 6
    
    def set_session_attribute(obj, attribute):
        attr = getattr(obj, attribute)
        if hasattr(attr, '__call__'):  # may use `callable()`
            return attr()
        return attr
    
    >> thing = Thing()
    >>> set_session_attribute(thing, 'legs')
    4
    >>> set_session_attribute(thing, 'length')
    6