Skip to content

O05: what a dot does - #135

Merged
tamnd merged 1 commit into
mainfrom
lesson/o05-what-a-dot-does
Aug 31, 2026
Merged

O05: what a dot does#135
tamnd merged 1 commit into
mainfrom
lesson/o05-what-a-dot-does

Conversation

@tamnd

@tamnd tamnd commented Aug 31, 2026

Copy link
Copy Markdown
Owner

The fifth lesson of the object model part. O04 built the list of classes a name gets looked up in. This one is about the function that reads that list, and about the three other places it looks before and after.

The point of the lesson is that four separate rules people memorise about attributes are all the same hundred and twenty line function, read in order.

What is in it:

  • _PyObject_GenericGetAttrWithDict does four things: walk the type's MRO, use what it found if that is a data descriptor, otherwise use the instance dict, otherwise fall back on what the MRO found and raise if there was nothing
  • the same name put on the type and in the instance dict three different ways, showing that a data descriptor wins, a non data descriptor loses, and the instance dict entry is left alone either way
  • which is also why obj.method = something shadows a method for one object while obj.some_property = something calls a setter instead
  • __getattribute__ is the slot and runs for everything including __dict__, and the lesson shows an object you can no longer inspect through ordinary syntax because of it
  • __getattr__ is not a slot at all, has no default, and gets a different dispatcher installed which removes itself from the slot when there is nothing to dispatch to
  • a class lookup runs _Py_type_getattro_stackref instead, checks the metaclass MRO first, and calls any descriptor with None where the instance would be, which is the difference between Holder.watched and Holder().watched
  • the two AttributeError messages are different because they come from different functions, and the did you mean suggestion is not in the message at all: it is computed from the obj attribute when a traceback is formatted
  • the type attribute cache, four thousand and ninety six entries keyed on a version tag, with no invalidation pass, because zeroing tp_version_tag on a class and its subclasses is enough to make every entry stop matching
  • and the observable end of that: the same LOAD_ATTR turning into LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_SLOT, LOAD_ATTR_METHOD_WITH_VALUES or LOAD_ATTR_MODULE, then failing its guard when the class changes, dropping back to the general form, and settling on LOAD_ATTR_PROPERTY

Fourteen citations against v3.15.0rc1, including _PyObject_GenericGetAttrWithDict, _Py_slot_tp_getattr_hook, _Py_type_getattro_stackref, _PyType_LookupStackRefAndVersion, type_modified_unlocked, MCACHE_CACHEABLE_NAME, MAX_VERSIONS_PER_CLASS and PyDescr_IsData.

Six diagrams. One cell differs between 3.14 and 3.15, the wording of the did you mean suggestion, and it carries a note. Everything else prints the same on both and in the Pyodide browser probe, specialised opcodes included.

One glossary term: data descriptor. GLOSSARY.md is now 116.

Part of #23.

The fifth lesson of the object model part. O04 built the list of classes a
name is looked up in, this one is about the function that reads that list
and the three other places it checks before and after.

PyObject_GenericGetAttr does four things in a fixed order, and the whole of
the precedence rule people memorise is the position of one if statement.
The lesson puts the same name on the type and in the instance dict three
different ways and shows which one wins each time.

Also here: the two attribute hooks and why they are not variants of each
other, the separate function a class lookup goes through, where the did you
mean suggestion is really computed, the version tag that makes the cache
work by going stale rather than being swept, and the four different
specialised instructions the same line of Python turns into.

Fourteen citations against v3.15.0rc1, six diagrams. One cell differs
between 3.14 and 3.15, the suggestion wording, and it says so.

One glossary term: data descriptor. GLOSSARY.md is now 116.
@tamnd tamnd added kind/lesson A chapter: prose, notebook, experiments, boss fight area/objects The object model, types, slots and the builtin types labels Aug 31, 2026
@tamnd
tamnd merged commit 10a0d45 into main Aug 31, 2026
16 checks passed
@tamnd
tamnd deleted the lesson/o05-what-a-dot-does branch August 31, 2026 16:47
@tamnd tamnd mentioned this pull request Aug 31, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/objects The object model, types, slots and the builtin types kind/lesson A chapter: prose, notebook, experiments, boss fight

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant