Toothpick Anemone
Toothpick Anemone

Reputation: 4636

How can we consistently override/overload python's dot operator, `__getattrribute__`?

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

Answers (1)

user2357112
user2357112

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

Related Questions