Learning Python - Mark Lutz [519]
X.__call__() # __call__? (explicit, not inherited)
print(X.__str__()) # __str__? (explicit, inherited from type)
print(X) # __str__? (implicit via built-in)
When run under Python 2.6, __getattr__ does receive a variety of implicit attribute fetches for built-in operations, because Python looks up such attributes in instances normally. Conversely, __getattribute__ is not run for any of the operator overloading names, because such names are looked up in classes only:
C:\misc> c:\python26\python getattr.py
GetAttr===========================================
getattr: other
__len__: 42
getattr: __getitem__
getattr: __coerce__
getattr: __add__
getattr: __call__
getattr: __call__
getattr: __str__
[Getattr str]
getattr: __str__
[Getattr str]
GetAttribute======================================
getattribute: eggs
getattribute: spam
getattribute: other
__len__: 42
fail []
fail +
fail ()
getattribute: __call__
getattribute: __str__
[GetAttribute str]
<__main__.GetAttribute object at 0x025EA1D0>
Note how __getattr__ intercepts both implicit and explicit fetches of __call__ and __str__ in 2.6 here. By contrast, __getattribute__ fails to catch implicit fetches of either attribute name for built-in operations.
Really, the __getattribute__ case is the same in 2.6 as it is in 3.0, because in 2.6 classes must be made new-style by deriving from object to use this method. This code’s object derivation is optional in 3.0 because all classes are new-style.
When run under Python 3.0, though, results for __getattr__ differ—none of the implicitly run operator overloading methods trigger either attribute interception method when their attributes are fetched by built-in operations. Python 3.0 skips the normal instance lookup mechanism when resolving such names:
C:\misc> c:\python30\python getattr.py
GetAttr===========================================
getattr: other
__len__: 42
fail []
fail +
fail ()
getattr: __call__
<__main__.GetAttr object at 0x025D17F0>
<__main__.GetAttr object at 0x025D17F0>
GetAttribute======================================
getattribute: eggs
getattribute: spam
getattribute: other
__len__: 42
fail []
fail +
fail ()
getattribute: __call__
getattribute: __str__
[GetAttribute str]
<__main__.GetAttribute object at 0x025D1870>
We can trace these outputs back to prints in the script to see how this works:
__str__ access fails to be caught twice by __getattr__ in 3.0: once for the built-in print, and once for explicit fetches because a default is inherited from the class (really, from the built-in object, which is a superclass to every class).
__str__ fails to be caught only once by the __getattribute__ catchall, during the built-in print operation; explicit fetches bypass the inherited version.
__call__ fails to be caught in both schemes in 3.0 for built-in call expressions, but it is intercepted by both when fetched explicitly; unlike with __str__, there is no inherited __call__ default to defeat __getattr__.
__len__ is caught by both classes, simply because it is an explicitly defined method in the classes themselves—its name it is not routed to either __getattr__ or __getattribute__ in 3.0 if we delete the class’s __len__ methods.
All other built-in operations fail to be intercepted by both schemes in 3.0.
Again, the net effect is that operator overloading methods implicitly run by built-in operations are never routed through either attribute interception method in 3.0: Python 3.0 searches for such attributes in classes and skips instance lookup entirely.
This makes delegation-based wrapper classes more difficult to code in 3.0—if wrapped classes may contain operator overloading methods, those methods must be redefined redundantly in the wrapper class in order to delegate to the wrapped object. In general delegation tools, this can add many extra methods.
Of course, the addition of such methods can be partly automated by tools that augment classes with new methods (the class decorators and metaclasses of the next two chapters might help here). Moreover,