I noticed that contrary to the classmethod
and staticmethod
decorators, the property
decorator overrides the object.__getattribute__
method:
>>> list(vars(classmethod))
['__new__', '__repr__', '__get__', '__init__', '__func__', '__wrapped__', '__isabstractmethod__', '__dict__', '__doc__']
>>> list(vars(staticmethod))
['__new__', '__repr__', '__call__', '__get__', '__init__', '__func__', '__wrapped__', '__isabstractmethod__', '__dict__', '__doc__']
>>> list(vars(property))
['__new__', '__getattribute__', '__get__', '__set__', '__delete__', '__init__', 'getter', 'setter', 'deleter', '__set_name__', 'fget', 'fset', 'fdel', '__doc__', '__isabstractmethod__']
The functionality of the property
decorator doesn’t seem to require this override (cf. the equivalent Python code in the Descriptor HowTo Guide). So which behaviour exactly does this override implement? Please provide a link to the corresponding C code in the CPython repository, and optionally an equivalent Python code.
To refresh yourself on what __getattribute__
is and how this is implemented in CPython, please refer to this very excellent answer first, as that answers contains all the detailed background information on what to look for in the CPython source, and the paragraphs below will reference those details without further explanations.
In CPython, all builtin types (e.g. object
, int
, str
, float
) all follow a standard way on how they are defined. These builtin types with their dunder methods are all slotted in the underlying C implementation, for example float
simply reference the default PyObject_GenericGetAttr
for tp_getattro
, and this is exactly the same for property
. Essentially, the descriptor howto guide is correct in omitting any references to __getattribute__
, as its presence is nothing more than an artifact on how builtin types in Python are defined (i.e. they all point to the same generic implementation for "the normal way of looking for object attributes" if it's just a pointer to the aptly named PyObject_GenericGetAttr
).
As for why classmethod
does not have __getattribute__
appear as part of its vars
output, that's because the definition for classmethod
does not include tp_getattro
nor tp_getattr
, but it just means that its __getattribute__
falls back to its parent's, which is object.__getattribute__
(check in Python for output of classmethod.__getattribute__ is object.__getattribute__
). As for why classmethod
is defined without the explicit linkage while various other builtin types define an explicit linkage to PyObject_GenericGetAttr
(despite the outcome of either decisions being functionally the same in nearly all code usage), you may have to ask the developers themselves.
Addendum: though at some point 12 years ago (2012), around the time of Python 3.2 was being developed, classmethod
did have tp_getattro
set, but subsequent to that commit 2cf936fe7a55
(included since 2.7.5 and 3.3.0a1) - the commit message has no hints as to why it was simply changed to "use defaults". As for float
and property
type, the change was introduced way back in the merge commit 6d6c1a35e08b
from nearly 22 years ago (2001) that brought in the changes outlined in PEP-252 which is the descriptor protocol itself. Note that it set all tp_getattro
to PyObject_GenericGetAttr
for standard types that didn't have it defined before (and likewise for classmethod
which is where this type made its debut).