sergzach
sergzach

Reputation: 6754

Does getattr() always call existing __getattr__() even if an attribute exists?

Is it true?

I have a difficulty in the next code. I want to call some methods later, in the execute() call. But surprisingly python calls the default-method __getattr__ when I call getattr(self,...) instead of firstly searching for methods __m1 and __m2.

class MyApi(object):
    "Class with API."

    def __init__(self):
        self.methods = []


    class Callable(object):
        def __init__(self, obj, name):
            self.obj = obj
            self.name = name            

        def __call__(self, *args, **kwargs):            
            self.obj.methods.append((self.name, args, kwargs,))


    def __m1(self, name):
        print "m1: name is %s" % name
        exit(0)


    def __m2(self, *args, **kwargs):
        print "m2: args is {} and kwargs is {}".format(args, kwargs)
        exit(0)


    def execute(self):
        for meth in self.methods:
            meth_name = '__' + meth[0]          
            print meth_name
            getattr(self, meth_name)(*meth[1], **meth[2]) # it seems to call __getattr__, not existing __m1 and __m2

        self.methods = []

    def __getattr__(self, name):
        return MyApi.Callable(self, name)



api = MyApi()

#import pdb; pdb.set_trace()

api.m1('Serg')
api.m2('John', 'Harrison', **{'key1': 1, 'key2': 2})

api.execute()

The result of a discussion.

We agreed that the problem was in two underscores which hide the attributes __m1 and __m2.

Upvotes: 2

Views: 2953

Answers (2)

MisterMiyagi
MisterMiyagi

Reputation: 50066

From the docs:

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). [...] Note that if the attribute is found through the normal mechanism, __getattr__() is not called.

So no, it should not be called under normal circumstances.


If you are having trouble that __getattr__ is called for internal methods, you can always use object.__getattr__(self, attr_name) to use python's default lookup.


If you do experience such a problem, you might have a problem with name mangling of dunder names. If a class attribute starts with double underscores, python will mangle the name for accesses from anything but instances of the class. This includes child classes! Use single underscores for "private" methods/attributes, unless you really need the functionality.

Upvotes: 4

Juergen
Juergen

Reputation: 12728

No.

If an attribute exists, __getattr__ is not called.

When you want such a behavior, you must use the method __getattribute__.

It seems to me, that your problem her is (as MisterMiyagi correctly pointed out), that the method names __m1 and __m2 (btw: what is the correct name -- in one position you call it m1 in the other __m1 -- that is inconsistent) will be mangled by the system. Meaning, the system sees them as _<classname>__<methodname> or _MyApi__m1. So, when using "getattr", you must use the mangled names.

Upvotes: 1

Related Questions