12

In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the __getattribute__() method even of the object’s metaclass.

The docs mention special methods such as __hash__, __repr__ and __len__, and I know from experience it also includes __iter__ for Python 2.7.

To quote an answer to a related question:

"Magic __methods__() are treated specially: They are internally assigned to "slots" in the type data structure to speed up their look-up, and they are only looked up in these slots."

In a quest to improve my answer to another question, I need to know: Which methods, specifically, are we talking about?

Community
  • 1
  • 1
porgarmingduod
  • 7,668
  • 10
  • 50
  • 83
  • Which methods are assigned to slots? – John Mee Oct 13 '12 at 11:47
  • 7
    I think every method listed [here](http://docs.python.org/reference/datamodel.html#specialnames). Anyway take into account that this does only apply if you call the method using "its syntax". For example `a+5` will not call `__getattribute__`, while `a.__add__(5)` *will* call it. Fundamentally `__getattribute__` is called whenever you use the dot(`.`) to access an attribute. – Bakuriu Oct 13 '12 at 12:12
  • @Bakuriu: Very informative, thank you. – porgarmingduod Oct 13 '12 at 12:16
  • @Bakuriu: can you put your comments in an answer please? Also, note that Python 3 behaves the same as you described, but has a much clearer documentation on this point. – max Oct 25 '12 at 06:42
  • @Bakuriu `__getattribute__` also have used with `hasattr(obj, method)` method – Dmitry Zagorulkin Oct 25 '12 at 07:49
  • @ZagorulkinDmitry You're right! I've added a note on this in my answer. – Bakuriu Oct 25 '12 at 08:14

3 Answers3

4

You can find an answer in the python3 documentation for object.__getattribute__, which states:

Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).

Note

This method may still be bypassed when looking up special methods as the result of implicit invocation via language syntax or built-in functions. See Special method lookup.

also this page explains exactly how this "machinery" works. Fundamentally __getattribute__ is called only when you access an attribute with the .(dot) operator(and also by hasattr as Zagorulkin pointed out).

Note that the page does not specify which special methods are implicitly looked up, so I deem that this hold for all of them(which you may find here.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
2

Checked in 2.7.9

Couldn't find any way to bypass the call to __getattribute__, with any of the magical methods that are found on object or type:

# Preparation step: did this from the console
# magics = set(dir(object) + dir(type))
# got 38 names, for each of the names, wrote a.<that_name> to a file
# Ended up with this:

a.__module__
a.__base__
#...

Put this at the beginning of that file, which i renamed into a proper python module (asdf.py)

global_counter = 0

class Counter(object):
    def __getattribute__(self, name):
        # this will count how many times the method was called
        global global_counter
        global_counter += 1
        return super(Counter, self).__getattribute__(name)

a = Counter()
# after this comes the list of 38 attribute accessess
a.__module__
#...
a.__repr__
#...

print global_counter  # you're not gonna like it... it printer 38

Then i also tried to get each of those names by getattr and hasattr -> same result. __getattribute__ was called every time.

So if anyone has other ideas... I was too lazy to look inside C code for this, but I'm sure the answer lies somewhere there.

So either there's something that i'm not getting right, or the docs are lying.

vlad-ardelean
  • 7,480
  • 15
  • 80
  • 124
  • 1
    If I remember correctly, my question was about `repr(a)` as opposed to `a.__repr__()`. It's been a while since I was into this stuff, but I'm guessing you get the getattribute version because you look up attributes, whereas the built-in methods we are talking about (the global `repr`, not `__repr__`) bypasses the existance of `__repr__`. – porgarmingduod Jan 05 '15 at 11:55
  • Override `__getattribute__` to issue a print whenever it's called and then do: `Counter('a') + Counter('a')` and see that it doesn't print anything. Because it's bypassing the standard name look up and hitting `Counter.__dict__['__add__']` directly or some such silly thing in the name of optimization. – justanr Mar 05 '15 at 16:56
-1

super().method will also bypass __getattribute__. This atrocious code will run just fine (Python 3.11).

class Base:
    def print(self):
        print("whatever")

    def __getattribute__(self, item):
        raise Exception("Don't access this with a dot!")


class Sub(Base):
    def __init__(self):
        super().print()

a = Sub()
# prints 'whatever'
a.print()
# Exception Don't access this with a dot!
Joren Boulanger
  • 111
  • 1
  • 8