From e4efe6871bb92e2ebe42774d5b6ffa814fafc7d4 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 19 Feb 2022 19:09:58 -0600 Subject: [PATCH 1/2] Improve wording and accuracy of the __getattr__ wording --- Doc/howto/descriptor.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index f8b1e00d96fadc..7cf8f5a6dde45a 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -696,10 +696,14 @@ a pure Python equivalent: >>> b.g == b['g'] == ('getattr_hook', b, 'g') True +Note, there is no `__getattr__` hook in the :meth:`__getattribute__` code. +That is why calling :meth:`__getattribute__` directly or with +``super().__getattribute__`` will bypass `__getattr__` entirely. -Interestingly, attribute lookup doesn't call :meth:`object.__getattribute__` -directly. Instead, both the dot operator and the :func:`getattr` function -perform attribute lookup by way of a helper function: +Instead, it is the dot operator and the :func:`getattr` function that are +responsible for invoking `__getattr__` whenever :meth:`__getattribute__` +raises an :exc:`AttributeError`. Their logic is encapsulated in a helper +function: .. testcode:: @@ -744,12 +748,6 @@ perform attribute lookup by way of a helper function: ... AttributeError: 'ClassWithoutGetAttr' object has no attribute 'z' -So if :meth:`__getattr__` exists, it is called whenever :meth:`__getattribute__` -raises :exc:`AttributeError` (either directly or in one of the descriptor calls). - -Also, if a user calls :meth:`object.__getattribute__` directly, the -:meth:`__getattr__` hook is bypassed entirely. - Invocation from a class ----------------------- From a65c47a9eea71883816d719d401b52a99ca6b7da Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sat, 19 Feb 2022 19:31:17 -0600 Subject: [PATCH 2/2] Add role markup for __getattr__ --- Doc/howto/descriptor.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/howto/descriptor.rst b/Doc/howto/descriptor.rst index 7cf8f5a6dde45a..f2e2f7ee68c2e2 100644 --- a/Doc/howto/descriptor.rst +++ b/Doc/howto/descriptor.rst @@ -696,12 +696,12 @@ a pure Python equivalent: >>> b.g == b['g'] == ('getattr_hook', b, 'g') True -Note, there is no `__getattr__` hook in the :meth:`__getattribute__` code. -That is why calling :meth:`__getattribute__` directly or with -``super().__getattribute__`` will bypass `__getattr__` entirely. +Note, there is no :meth:`__getattr__` hook in the :meth:`__getattribute__` +code. That is why calling :meth:`__getattribute__` directly or with +``super().__getattribute__`` will bypass :meth:`__getattr__` entirely. Instead, it is the dot operator and the :func:`getattr` function that are -responsible for invoking `__getattr__` whenever :meth:`__getattribute__` +responsible for invoking :meth:`__getattr__` whenever :meth:`__getattribute__` raises an :exc:`AttributeError`. Their logic is encapsulated in a helper function: