Reputation: 4636
Assume that we are using python 3.x
or newer, not python 2.x
Python, like many languages, has a dot-operator:
# Create a new instance of the Rectangle class
robby = Rectangle(3, 10)
# INVOKE THE DOT OPERATOR
x = robby.length
Python's dot-operator is sometimes implemented as __getattribute__
.
The following is equivalent to x = robby.length
:
x = Rectangle.__getattribute__(robby, "length")
However, the dot-operator is not always implemented as __getattribute__
.
Python has "magic methods"
A magic methods is any method whose name begins and ends with two underscore characters.
__len__()
is an example of a magic method.
You can get a list of most of python's magic methods by executing the following code:
print("\n".join(filter(lambda s: s.startswith("__"), dir(int))))
The output is:
__abs__
__add__
__and__
__bool__
__ceil__
__class__
__delattr__
__dir__
__divmod__
__doc__
__eq__
__float__
[... truncated / abridged ...]
__rtruediv__
__rxor__
__setattr__
__sizeof__
__str__
__sub__
__subclasshook__
__truediv__
__trunc__
__xor__
Suppose we write a class named Rectangle
, sub-class of object
.
Then my attempts to override object.__getattribute__
inside of the Rectangle
class, usually fail.
The following shows an example of a class where python sometimes ignores an overridden dot-operator:
class Klass:
def __getattribute__(self, attr_name):
return print
obj = Klass()
obj.append() # WORKS FINE. `obj.append == print`
obj.delete() # WORKS FINE. `obj.delete == print`
obj.length # WORKS FINE
obj.x # WORKS FINE
# None of the following work, because they
# invoke magic methods.
# The following line is similar to:
# x = Klass.__len__(obj)
len(obj)
# obj + 5
# is similar to:
# x = Klass.__add__(obj, 5)
x = obj + 5
# The following line is similair to:
# x = Klass.__radd__(obj, 2)
x = 2 + obj
There is more than one way to override python's dot operator.
What is an example of one way to do it which is readable, clean, and consistent?
By consistent, I mean that our custom dot operator gets called whenever .
is used in source code, no matter whether the method is a magic method or not.
I am unwilling to manually type-in every single magic method under the sun.
I don't want to see thousands of lines of code which looks like:
def __len__(*args, **kwargs):
return getattr(args[0], "__len__")(*args, **kwargs)
I understand the distinction between __getattr__
and __getattribute__
Overriding __getattribute__
instead of __getattr__
is not the issue at hand.
Upvotes: 0
Views: 1136
Reputation: 281683
__getattribute__
already does what you're literally asking for - overriding __getattribute__
is all you need to handle all uses of the .
operator. (Strictly speaking, Python will fall back to __getattr__
if __getattribute__
fails, but you don't have to worry about that as long as you don't implement __getattr__
.)
You say you want your operator to be called "whenever .
is used in source code", but len
, +
, and all the other things you're worried about don't use .
. There is no .
in len(obj)
, obj + 5
, or 2 + obj
.
Most magic method lookups don't use attribute access. If you actually look up yourobj.__len__
or yourobj.__add__
, that will go through attribute access, and your __getattribute__
will be invoked, but when Python looks up a magic method to implement language functionality, it does a direct search of the object's type's MRO. The .
operator is not involved.
There is no way to override magic method lookup. That's a hardcoded process with no override hook. The closest thing you can do is override individual magic methods to delegate to __getattribute__
, but that's not the same thing as overriding magic method lookup (or overriding .
), and it's easy to get infinite recursion bugs that way.
If all you really want to do is avoid repetitive individual magic method overrides, you could put them in a class decorator or mixin.
Upvotes: 2