Cristian Suarez
Cristian Suarez

Reputation: 157

How can I use __getattr__ in functions in Python 3.1?

I'm trying to redifine the __getattr__ method, in a function.

I've tried this code:

def foo():
    print("foo")

def addMethod(obj, func):
    setattr(obj, func.__name__, types.MethodType(func, obj))

def __getattr__(obj, name):
    print(name)

addMethod(foo, __getattr__)

foo.bar

but I get this error:

Traceback (most recent call last):
  File blah blah, line 14, in <module>
    foo.bar
AttributeError: 'function' object has no attribute 'bar'

I've inspected the "foo" function and it really has the method bounded to it, but it seems that if you set it dynamically, __getattr__ won't get called. If I do the same thing to a class, if I set the __getattr__ using my "addMethod" function, the instance won't call the __getattr__ too!, so the problem must be the dynamic call!

BUT if I put the __getattr__ in the definition of the class, it will work, obviously.

The question is, how can I put the __getattr__ to the function to make it work? I can't put it from the beginning because it's a function!, I don't know how to do this

Thanks!

Upvotes: 1

Views: 263

Answers (2)

Ethan Furman
Ethan Furman

Reputation: 69001

Your problem is that Python will only look up __xxx__ magic methods on the class of an object, not the object itself. So even though you are setting __getattr__ on the instance of foo, it will never be automatically called.

That is also the problem you are having with class foo:

class foo():
    pass

def addMethod(obj, func):
    setattr(obj, func.__name__, func)

def __getattr__(obj, name):
    print(name)

addMethod(foo, __getattr__)

foo.bar

Here Python is looking up bar on the class object foo which means . . .

the __getattr__ method you added to foo is not getting called, instead Python is looking at foo's metaclass, which is type.

If you change that last line to

foo().bar

and get an instance of the class of foo, it will work.

Upvotes: 1

Cat Plus Plus
Cat Plus Plus

Reputation: 129754

Well, you don't. If you want attributes, make a class. If you want instances to be callable, define __call__ for it.

class foo:
    def __call__(self):
        print("foo")

    def __getattr__(self, name):
        print(name)

f = foo()
f()   # foo
f.bar # bar

Upvotes: 2

Related Questions