Skip to content

Commit 12a2e41

Browse files
authored
Improve discussion about how __getattr__ is invoked. (GH-31435)
1 parent 0a8a8e7 commit 12a2e41

File tree

1 file changed

+7
-9
lines changed

1 file changed

+7
-9
lines changed

Doc/howto/descriptor.rst

+7-9
Original file line numberDiff line numberDiff line change
@@ -696,10 +696,14 @@ a pure Python equivalent:
696696
>>> b.g == b['g'] == ('getattr_hook', b, 'g')
697697
True
698698

699+
Note, there is no :meth:`__getattr__` hook in the :meth:`__getattribute__`
700+
code. That is why calling :meth:`__getattribute__` directly or with
701+
``super().__getattribute__`` will bypass :meth:`__getattr__` entirely.
699702

700-
Interestingly, attribute lookup doesn't call :meth:`object.__getattribute__`
701-
directly. Instead, both the dot operator and the :func:`getattr` function
702-
perform attribute lookup by way of a helper function:
703+
Instead, it is the dot operator and the :func:`getattr` function that are
704+
responsible for invoking :meth:`__getattr__` whenever :meth:`__getattribute__`
705+
raises an :exc:`AttributeError`. Their logic is encapsulated in a helper
706+
function:
703707

704708
.. testcode::
705709

@@ -744,12 +748,6 @@ perform attribute lookup by way of a helper function:
744748
...
745749
AttributeError: 'ClassWithoutGetAttr' object has no attribute 'z'
746750

747-
So if :meth:`__getattr__` exists, it is called whenever :meth:`__getattribute__`
748-
raises :exc:`AttributeError` (either directly or in one of the descriptor calls).
749-
750-
Also, if a user calls :meth:`object.__getattribute__` directly, the
751-
:meth:`__getattr__` hook is bypassed entirely.
752-
753751

754752
Invocation from a class
755753
-----------------------

0 commit comments

Comments
 (0)