The python documentation states that __getattribute__ may be bypassed when looking up special methods. This is the result of implicit invocation via language syntax or built-in functions.
For example,
elem = container[0]
is not the same as:
elem = container.__getattribute__('__getitem__')[0]
Below is a another example:
class WrappedList:
def __init__(self):
object.__setattr__(self, 'interal_list', ['apple', 'pear', 'orange'])
def __getattribute__(self, attr_name):
interal_list = object.__getattribute__(self, 'interal_list')
attr = getattr(interal_list, attr_name)
return attr
wl = WrappedList()
print("\nSTART TEST 01 ------------------------")
try:
print(wl[0]) # throws TypeError: 'WrappedList' object does not support indexing
except TypeError as e:
print(e)
print("\nSTART TEST 02 ------------------------")
try:
getitem = getattr(wl, '__getitem__')
print(getitem(0)) # works just fine
except TypeError as e:
print(e)
I would like to write a class named something like MagicOverrider where any class which inherits from MagicOverrider always calls __getattribute__, instead of going around it. My question is how can we do this?
I tried the following:
class MagicOverrider:
def __call__(self, *args, **kwargs):
f = getattr(self, '__call__')
return f(*args, **kwargs)
def __iter__(self, *args, **kwargs):
f = getattr(self, '__iter__')
return f(*args, **kwargs)
def __getitem__(self, *args, **kwargs):
f = getattr(self, '__getitem__')
return f(*args, **kwargs)
def __setitem__(self, *args, **kwargs):
f = getattr(self, '__setitem__')
return f(*args, **kwargs)
def __add__(self, *args, **kwargs):
f = getattr(self, '__add__')
return f(*args, **kwargs)
def __sub__(self, *args, **kwargs):
f = getattr(self, '__sub__')
return f(*args, **kwargs)
def __mul__(self, *args, **kwargs):
f = getattr(self, '__mul__')
return f(*args, **kwargs)
def __truediv__(self, *args, **kwargs):
f = getattr(self, '__truediv__')
return f(*args, **kwargs)
def __floordiv__(self, *args, **kwargs):
f = getattr(self, '__floordiv__')
return f(*args, **kwargs)
def __mod__(self, *args, **kwargs):
f = getattr(self, '__mod__')
return f(*args, **kwargs)
def __divmod__(self, *args, **kwargs):
f = getattr(self, '__divmod__')
return f(*args, **kwargs)
def __pow__(self, *args, **kwargs):
f = getattr(self, '__pow__')
return f(*args, **kwargs)
def __lshift__(self, *args, **kwargs):
f = getattr(self, '__lshift__')
return f(*args, **kwargs)
def __rshift__(self, *args, **kwargs):
f = getattr(self, '__rshift__')
return f(*args, **kwargs)
def __and__(self, *args, **kwargs):
f = getattr(self, '__and__')
return f(*args, **kwargs)
def __xor__(self, *args, **kwargs):
f = getattr(self, '__xor__')
return f(*args, **kwargs)
def __or__(self, *args, **kwargs):
f = getattr(self, '__or__')
return f(*args, **kwargs)
def __radd__(self, *args, **kwargs):
f = getattr(self, '__radd__')
return f(*args, **kwargs)
def __rsub__(self, *args, **kwargs):
f = getattr(self, '__rsub__')
return f(*args, **kwargs)
def __rmul__(self, *args, **kwargs):
f = getattr(self, '__rmul__')
return f(*args, **kwargs)
def __rtruediv__(self, *args, **kwargs):
f = getattr(self, '__rtruediv__')
return f(*args, **kwargs)
def __rfloordiv__(self, *args, **kwargs):
f = getattr(self, '__rfloordiv__')
return f(*args, **kwargs)
def __rmod__(self, *args, **kwargs):
f = getattr(self, '__rmod__')
return f(*args, **kwargs)
def __rdivmod__(self, *args, **kwargs):
f = getattr(self, '__rdivmod__')
return f(*args, **kwargs)
def __rpow__(self, *args, **kwargs):
f = getattr(self, '__rpow__')
return f(*args, **kwargs)
def __rlshift__(self, *args, **kwargs):
f = getattr(self, '__rlshift__')
return f(*args, **kwargs)
def __rrshift__(self, *args, **kwargs):
f = getattr(self, '__rrshift__')
return f(*args, **kwargs)
def __rand__(self, *args, **kwargs):
f = getattr(self, '__rand__')
return f(*args, **kwargs)
def __rxor__(self, *args, **kwargs):
f = getattr(self, '__rxor__')
return f(*args, **kwargs)
def __neg__(self, *args, **kwargs):
f = getattr(self, '__neg__')
return f(*args, **kwargs)
def __pos__(self, *args, **kwargs):
f = getattr(self, '__pos__')
return f(*args, **kwargs)
def __abs__(self, *args, **kwargs):
f = getattr(self, '__abs__')
return f(*args, **kwargs)
def __invert__(self, *args, **kwargs):
f = getattr(self, '__invert__')
return f(*args, **kwargs)
def __complex__(self, *args, **kwargs):
f = getattr(self, '__complex__')
return f(*args, **kwargs)
def __int__(self, *args, **kwargs):
f = getattr(self, '__int__')
return f(*args, **kwargs)
def __float__(self, *args, **kwargs):
f = getattr(self, '__float__')
return f(*args, **kwargs)
def __round__(self, *args, **kwargs):
f = getattr(self, '__round__')
return f(*args, **kwargs)
def __index__(self, *args, **kwargs):
f = getattr(self, '__index__')
return f(*args, **kwargs)
def __eq__(self, *args, **kwargs):
f = getattr(self, '__eq__')
return f(*args, **kwargs)
def __ne__(self, *args, **kwargs):
f = getattr(self, '__ne__')
return f(*args, **kwargs)
def __lt__(self, *args, **kwargs):
f = getattr(self, '__lt__')
return f(*args, **kwargs)
def __le__(self, *args, **kwargs):
f = getattr(self, '__le__')
return f(*args, **kwargs)
def __gt__(self, *args, **kwargs):
f = getattr(self, '__gt__')
return f(*args, **kwargs)
def __ge__(self, *args, **kwargs):
f = getattr(self, '__ge__')
return f(*args, **kwargs)
def __bool__(self, *args, **kwargs):
f = getattr(self, '__bool__')
return f(*args, **kwargs)
def __new__(self, *args, **kwargs):
f = getattr(self, '__new__')
return f(*args, **kwargs)
def __del__(self, *args, **kwargs):
f = getattr(self, '__del__')
return f(*args, **kwargs)
def __slots__(self, *args, **kwargs):
f = getattr(self, '__slots__')
return f(*args, **kwargs)
def __hash__(self, *args, **kwargs):
f = getattr(self, '__hash__')
return f(*args, **kwargs)
def __instancecheck__(self, *args, **kwargs):
f = getattr(self, '__instancecheck__')
return f(*args, **kwargs)
def __subclasscheck__(self, *args, **kwargs):
f = getattr(self, '__subclasscheck__')
return f(*args, **kwargs)
def __subclasshook__(self, *args, **kwargs):
f = getattr(self, '__subclasshook__')
return f(*args, **kwargs)
def __ror__(self, *args, **kwargs):
f = getattr(self, '__ror__')
return f(*args, **kwargs)
def __iadd__(self, *args, **kwargs):
f = getattr(self, '__iadd__')
return f(*args, **kwargs)
def __isub__(self, *args, **kwargs):
f = getattr(self, '__isub__')
return f(*args, **kwargs)
def __imul__(self, *args, **kwargs):
f = getattr(self, '__imul__')
return f(*args, **kwargs)
def __itruediv__(self, *args, **kwargs):
f = getattr(self, '__itruediv__')
return f(*args, **kwargs)
def __ifloordiv__(self, *args, **kwargs):
f = getattr(self, '__ifloordiv__')
return f(*args, **kwargs)
def __imod__(self, *args, **kwargs):
f = getattr(self, '__imod__')
return f(*args, **kwargs)
def __ipow__(self, *args, **kwargs):
f = getattr(self, '__ipow__')
return f(*args, **kwargs)
def __ilshift__(self, *args, **kwargs):
f = getattr(self, '__ilshift__')
return f(*args, **kwargs)
def __irshift__(self, *args, **kwargs):
f = getattr(self, '__irshift__')
return f(*args, **kwargs)
def __iand__(self, *args, **kwargs):
f = getattr(self, '__iand__')
return f(*args, **kwargs)
def __ixor__(self, *args, **kwargs):
f = getattr(self, '__ixor__')
return f(*args, **kwargs)
def __repr__(self, *args, **kwargs):
f = getattr(self, '__repr__')
return f(*args, **kwargs)
def __str__(self, *args, **kwargs):
f = getattr(self, '__str__')
return f(*args, **kwargs)
def __cmp__(self, *args, **kwargs):
f = getattr(self, '__cmp__')
return f(*args, **kwargs)
def __rcmp__(self, *args, **kwargs):
f = getattr(self, '__rcmp__')
return f(*args, **kwargs)
def __nonzero__(self, *args, **kwargs):
f = getattr(self, '__nonzero__')
return f(*args, **kwargs)
def __unicode__(self, *args, **kwargs):
f = getattr(self, '__unicode__')
return f(*args, **kwargs)
However, my solution has at least two problems:
- It won't work anymore if new magic methods are introduced in a future release of python
- the first line,
class MagicOverrider:, throwsTypeError: 'function' object is not iterable
There is no hook or option you can set to turn on regular attribute handling for all magic methods. The only way you could do that is by overriding all of them to delegate to your desired handling individually, which has a few important limitations, one of which you caught:
Your attempt failed because you tried to define
__slots__as an instance method.__slots__is not supposed to be any sort of method at all, and definitely not an instance method; it needs to be a sequence, and it needs to be processed at class definition time to decide the layout of instances of your class. You also tried to write__new__as if it was an instance method. Even if you hadn't made those mistakes, though, most of your methods would just end up in infinite recursive loops.