You have the right to question the documentation. I tried to browse CPython sources to find an explanation, but be warned: I am not an expert.
From my point of view, the attribute search and the __get__ descriptor occurs in instance_getattr2 (selected excerpts):
v = class_lookup(inst->in_class, name, &klass); if (v != NULL) { f = TP_DESCR_GET(v->ob_type); if (f != NULL) { PyObject *w = f(v, (PyObject *)inst, (PyObject *)(inst->in_class)); } }
So either I am missing something, or nothing in the implementation requires an object of a new style (which contradicts the documentation).
For the record, I tried recompiling Python to limit the descriptor call to new class class objects, but this actually caused a huge mess. In the process, I found out that the class methods themselves are implemented as descriptors: this is the mechanism used to return related or unrelated method objects depending on usage. For instance:
>>> class A: ... def foo(): ... pass ... >>> A.foo.__get__(None, A) <unbound method A.foo> >>> A.foo.__get__(A(), A) <bound method A.foo of <__main__.A instance at 0x000000000229CC48>>
As a result, it seems that preventing descriptor calls for object attributes or old-style classes also prevents method calls on them , at least with the implementation of CPython.
Once again, I'm not an expert, and this is the first time I'm immersed in a Python implementation, so I could have been mistaken. I requested a problem to try to clarify this.
source share