Data model and attribute resolution in subclasses
Terry Reedy
tjreedy at udel.edu
Fri Feb 28 13:04:23 EST 2020
On 2/28/2020 2:21 AM, Adam Preble wrote:
> I have been making some progress on my custom interpreter project but I found I have totally blown implementing proper subclassing in the data model. What I have right now is PyClass defining what a PyObject is. When I make a PyObject from a PyClass, the PyObject sets up a __dict__ that is used for attribute lookup. When I realized I needed to worry about looking up parent namespace stuff, this fell apart because my PyClass had no real notion of a namespace.
>
> I'm looking at the Python data model for inspiration. While I don't have to implement the full specifications, it helps me where I don't have an alternative. However, the data model is definitely a programmer document; it's one of those things where the prose is being very precise in what it's saying and that can foil a casual reading.
>
> Here's what I think is supposed to exist:
> 1. PyObject is the base.
> 2. It has an "internal dictionary." This isn't exposed as __dict__
The internal mapping *is* visible.
>>> object.__dict__
mappingproxy({'__repr__': <slot wrapper '__repr__' of 'object' objects>,
'__hash__': <slot wrapper '__hash__' of 'object' objects>, '__str__':
<slot wrapper '__str__' of 'object' objects>, '__getattribute__': <slot
wrapper '__getattribute__' of 'object' objects>, '__setattr__': <slot
wrapper '__setattr__' of 'object' objects>, '__delattr__': <slot wrapper
'__delattr__' of 'object' objects>, '__lt__': <slot wrapper '__lt__' of
'object' objects>, '__le__': <slot wrapper '__le__' of 'object'
objects>, '__eq__': <slot wrapper '__eq__' of 'object' objects>,
'__ne__': <slot wrapper '__ne__' of 'object' objects>, '__gt__': <slot
wrapper '__gt__' of 'object' objects>, '__ge__': <slot wrapper '__ge__'
of 'object' objects>, '__init__': <slot wrapper '__init__' of 'object'
objects>, '__new__': <built-in method __new__ of type object at
0x00007FFBEFE989A0>, '__reduce_ex__': <method '__reduce_ex__' of
'object' objects>, '__reduce__': <method '__reduce__' of 'object'
objects>, '__subclasshook__': <method '__subclasshook__' of 'object'
objects>, '__init_subclass__': <method '__init_subclass__' of 'object'
objects>, '__format__': <method '__format__' of 'object' objects>,
'__sizeof__': <method '__sizeof__' of 'object' objects>, '__dir__':
<method '__dir__' of 'object' objects>, '__class__': <attribute
'__class__' of 'object' objects>, '__doc__': 'The base class of the
class hierarchy.\n\nWhen called, it accepts no arguments and returns a
new featureless\ninstance that has no instance attributes and cannot be
given any.\n'})
The internal mapping is not an instance of dict, and need/should not be
as it is frozen.
>>> o = object()
>>> o.a = 3
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
o.a = 3
AttributeError: 'object' object has no attribute 'a'
When classes and objects do have a dict __dict__, __dict__ is not in
__dict__, to avoid recursion. gettattribute or __getattribute__ must
special case '__dict__'. I am guessing this as what must be from the
evidence, without seeing the actual code.
> 3. PyClass subclasses PyObject.
> 4. PyClass has a __dict__
>
> Is there a term for PyObject's internal dictionary. It wasn't called __dict__ and I think that's for good reasons. I guess the idea is a PyObject doesn't have a namespace, but a PyClass does (?).
>
> Now to look something up. I assume that __getattribute__ is supposed to do something like:
> 1. The PyClass __dict__ for the given PyObject is consulted.
> 2. The implementation for __getattribute__ for the PyObject will default to looking into the "internal dictionary."
> 3. Assuming the attribute is not found, the subclasses are then consulted using the subclass' __getattribute__ calls. We might recurse on this. There's probably some trivia here regarding multiple inheritance; I'm not entirely concerned (yet).
For non-reserved names Attribute lookup starts with the object dict,
then object class, then superclasses. Dunder names usually start with
the object class.
> 4. Assuming it's never found, then the user sees an AttributeError
>
> Would each of these failed lookups result in an AttributeError?
I presume so. They are caught and only re-raised only if there is no
where else to look.
--
Terry Jan Reedy
More information about the Python-list
mailing list