2

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.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
codyc4321
  • 9,014
  • 22
  • 92
  • 165

2 Answers2

5

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
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
1

Decorate those methods with @property, which you feel should have semantics of data rather than a method.

class Thing:
    @property
    def length(self):
        return somedata

    @property.setter
    def length(self, value):
        somename = value

t = Thing()
l0 = t.length
t.length = l1

So this is a getter. There is also a setter @property.setter, where you can do t.length = somedata instead of something like t.set_length(somedata)

C Panda
  • 3,297
  • 2
  • 11
  • 11