Search code examples
pythonclassgetattr

Access object methods from getattr()


I need to create a script that allows you to access object atributes dynamically from getattr() built-in function, but I'm having lots of troubles when trying to access the object methods, here is an example:

class Dog():
    def __init__(self):
        self.age = 12
        self.name = 'Bobby'
    
    def eat(self):
        print("I'm eating!")

If I then initialize the object and try to access their attributes:

mydog = Dog()

res = getattr(mydog, 'age')
print(res)
#--> return: 12

But if I try to access the eat method:

mydog = Dog()

res = getattr(mydog, 'eat()')
print(res)
#--> return: AttributeError: 'Dog' object has no attribute 'eat()'

I have already tried to write the method name without quotation marks, and the best solution that I found is to write the method without the parentheses and then access the function from res:

mydog = Dog()

res = getattr(mydog, 'eat')
res()
#--> return: I'm eating!

I could code a conditional that detects if res contains a variable or a function and call it in case it's a method, but is there a better solution to this?


Solution

  • Using the @property decorator on the method eat it will make it so that we can retrieve it without problems with getattr:

    class Dog():
        def __init__(self):
            self.age = 12
            self.name = 'Bobby'
    
        @property
        def eat(self):
            return "I'm eating!"
    
    mydog = Dog()
    res1 = getattr(mydog, 'age')
    res2 = getattr(mydog, 'name')
    res3 = getattr(mydog, 'eat')
    print(res1) #Output: 12
    print(res2) #Output: Bobby
    print(res3) #Output: I'm eating!
    

    If we don't make eat into a property, then calling getattr(mydog, "eat") will return a reference to the method without calling it. However, when you set @property in a method, that will create a getter of eat that will be called when you try to call that attribute.

    If we then want to extend the above, so that we can change the value of eat then we can also create a setter of eat:

    class Dog():
        def __init__(self):
            self._eat = "I'm eating!"
    
        @property
        def eat(self):
            return self._eat
        
        @eat.setter
        def eat(self, eat_string):
            self._eat = eat_string
    
    mydog = Dog()
    print(mydog.eat) #Output: I'm eating!
    mydog.eat = "Stop bothering me!"
    print(mydog.eat) #Output: Stop bothering me!