This recipe is how you can show that the Python operators and builtin functions directly use the special methods from an object's class, rather than using normal attribute lookup on the object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | # list of special methods from http://docs.python.org/py3k/reference/datamodel.html
names = {
"control": "{}.control", # a normal method, for comparison
"__new__": None,
#"__new__": "{}()", # if an instance of type
"__init__": None,
#"__init__": "{}()", # if an instance of type
"__prepare__": None,
#"__prepare__": "{}()", # if an instance of type
"__del__": "del {}",
"__repr__": "repr({})",
"__str__": "str({})",
"__format__": "'{{}}'.format({})",
"__lt__": "{} < 5",
"__le__": "{} <= 5",
"__eq__": "{} == 5",
"__ne__": "{} != 5",
"__gt__": "{} > 5",
"__ge__": "{} >= 5",
"__hash__": "hash({})",
"__bool__": "bool({})",
"__getattr__": None,
#"__getattr__": "{}.x",
"__getattribute__": None,
#"__getattribute__": "{}.x",
"__setattr__": "{}.x = 5",
"__delattr__": "del {}.x",
"__dir__": "dir({})",
"__get__": None,
"__set__": None,
"__delete__": None,
"__slots__": None,
"__call__": "{}()",
"__len__": "len({})",
"__getitem__": "{}[1]",
"__setitem__": "{}[1] = 5",
"__delitem__": "del {}[1]",
"__iter__": "iter({})",
"__reversed__": "reversed({})",
"__contains__": "5 in {}",
"__add__": "{} + 5",
"__sub__": "{} - 5",
"__mul__": "{} * 5",
"__truediv__": "{} / 5",
"__floordiv__": "{} // 5",
"__mod__": "{} % 5",
"__divmod__": "divmod({}, 5)",
"__pow__": "{}**5",
"__lshift__": "{} << 5",
"__rshift__": "{} >> 5",
"__and__": "{} & 5",
"__xor__": "{} ^ 5",
"__or__": "{} | 5",
"__radd__": "5 + {}",
"__rsub__": "5 - {}",
"__rmul__": "5 * {}",
"__rtruediv__": "5 / {}",
"__rfloordiv__": "5 // {}",
"__rmod__": "5 % {}",
"__rdivmod__": "divmod(5, {})",
"__rpow__": "5 ** {}",
"__rlshift__": "5 << {}",
"__rrshift__": "5 >> {}",
"__rand__": "5 & {}",
"__rxor__": "5 ^ {}",
"__ror__": "5 | {}",
"__iadd__": "{} += 5",
"__isub__": "{} -= 5",
"__imul__": "{} *= 5",
"__itruediv__": "{} /= 5",
"__ifloordiv__": "{} //= 5",
"__imod__": "{} %= 5",
"__ipow__": "{} **= 5",
"__ilshift__": "{} <<= 5",
"__irshift__": "{} >>= 5",
"__iand__": "{} &= 5",
"__ixor__": "{} ^= 5",
"__ior__": "{} |= 5",
"__neg__": "-{}",
"__pos__": "+{}",
"__abs__": "abs({})",
"__invert__": "~{}",
"__complex__": "complex({})",
"__int__": "int({})",
"__float__": "float({})",
"__round__": "round({})",
"__index__": "oct({})",
"__enter__": None,
"__exit__": None,
}
SOURCE = """\
def {0}(self, *args, **kwargs):
raise Called
Test{0} = type("Test{0}", (Test,), dict({0}={0}))
test{0} = Test{0}()
{1}
"""
class InstanceChecked(Exception): pass
class Called(Exception): pass
class Test:
def __getattribute__(self, name):
raise InstanceChecked
skipped = []
for name in names:
if not names[name]:
skipped.append(name)
continue
print("#########################")
print(" "+name)
source = SOURCE.format(name, names[name].format("test"+name))
try:
exec(source, globals(), globals())
except Called:
print(" Doing it")
except InstanceChecked:
print(" --- instance __getattribute__ called")
#except Exception:
# pass
print("#########################")
print("skipped {}: {}".format(len(skipped), skipped))
|
See http://docs.python.org/py3k/reference/datamodel.html#special-method-lookup for an explanation of special method lookup.
The special methods cover all of the Python operators, several other language features (like for loops), and a number of the builtin functions. If some functionality in CPython is written in C, it's probably using special method lookup.
Running the recipe you can see that these features don't use normal attribute lookup; they're pulling the methods directly from the object's type. And they don't even use normal attribute lookup for that. Instead, (in CPython) they effectively make direct calls on the functions stored in the PyTypeObject struct of the object's type.
So just to be clear, it isn't that the special methods are special on their own or that there is some kind of special casing for when a method has a double underscore at it's beginning and end. Rather, the related Python features simply use the attributes on the types and do it directly. You could do it too if you wanted to. It's just a more roundabout approach outside of C extension modules.
The builtins and operators do it because a handful of the special methods have to be done this way, so all of them are used this way.
FYI, starting in Python 3.2 the inspect module provides a function for a more static lookup along these lines (see http://docs.python.org/dev/library/inspect.html#inspect.getattr_static).