6

Let's say there is some class:

class Test():
    pass 

(1)Somewhere on SO and in documentation i read next:

mro() called at class instantiation, and its result is stored in __mro__.

Okay, that is still clear to me, cause in __mro__ something is really stored:

Test.__mro__                                                         
Out[48]: (__main__.Test, object)

Again, somewhere i read this:

To look up an attribute name Python searches:
a)Search the __dict__ of all metaclasses on the __mro__ found at C’s __class__.
b)If a data descriptor was found in step a, call its __get__() and exit.
c)Else, call a descriptor or return a value in the __dict__ of a class on C’s own __mro__.
d)Call a non-data descriptor found in step a.
e)Else, return Metaclass-tree values

I can find out that there is no __mro__ in Test.__dict__:

'__mro__' in Test.__dict__                                           
Out[49]: False

So accordingly to e clause from previous quote i guess that __mro__ should be taken from "Metaclass-tree values" and hence from type.__dict__

Really, there is __mro__ in type.__dict__:

["mro:<method 'mro' of 'type' objects>",
"__mro__:<member '__mro__' of 'type' objects>"]
  1. So what was mentioned above in (1) about mro() result stored in __mro__ attribute from documentation doesn't really works this way?

  2. How does <member '__mro__' of 'type' objects> results to (__main__.Test, object)?

Maybe you could show some source code to understand what really happens when i call Test.__mro__..

pospolitaki
  • 313
  • 2
  • 9
  • Note that ``Test.__dict__`` is missing all sorts of attributes – compare ``{attr: attr in Test.__dict__ for attr in dir(Test)}``. An "attribute of ``thing``" is *not* equivalent to a "key in ``thing.__dict__``". ``__mro__`` is just example of many. – MisterMiyagi Aug 12 '20 at 11:37

1 Answers1

4

The __mro__ "attribute" is a data descriptor, similar to property. Instead of fetching the __mro__ attribute value from __dict__, the descriptor fetches the value from another place or computes it. In specific, a <member '...' of '..' objects> indicates a descriptor that fetches the value from an VM-internal location – this is the same mechanism used by __slots__.

>>> class Bar:
...     __slots__ = "foo",
...
>>> Bar.foo
<member 'foo' of 'Bar' objects>
>>> 'foo' in Bar.__dict__
True

Descriptors are inherited without duplication, and thus do not appear explicitly on subclasses.

>>> class Foo(Bar):
...     __slots__ = ()
...
>>> Foo.foo
<member 'foo' of 'Bar' objects>
>>> 'foo' in Foo.__dict__
False

The precise working of such a member data descriptor is implementation defined. However, logically they work the same as a property using an internal storage:

class FooBar:
    def __init__(self, foo, bar):
        # attributes stored internally
        # the "_data" of a member is not visible
        self._data = [foo, bar]

    @property
    def foo(self):
        return self._data[0]

    @foo.setter
    def foo(self, value):
        self._data[0] = value

    @property
    def bar(self):
        return self._data[1]

    @bar.setter
    def bar(self, value):
        self._data[1] = value
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • sorry, but would you mind to expand on the phrase `a member indicates a descriptor that fetches the value from an VM-internal location` a bit? Does it mean that `type.__mro__` - it's member descriptor from `type.__dict__`, which just implemented in this way by default in Python's primitive `type` in `C code "level"` ? Cause in case of `class Foo` we have `` but we also have `'__slots__' in dir(Foo) -> True`, while `'__slots__' in dir(type) -> False`.. – pospolitaki Aug 12 '20 at 12:51
  • 1
    ``__slots__`` is a way to request creation of a ``member`` descriptor on a class. It is not the only one, and internally a ``member`` descriptor can be explicitly created. In fact, you can freely remove ``__slots__`` after the descriptor has been created without any ill effect. – MisterMiyagi Aug 12 '20 at 13:06
  • Could you say where to read more about `member` descriptors? – pospolitaki Aug 12 '20 at 13:14
  • 1
    AFAIK these are not succinctly documented anywhere – they are a combination of several advanced concepts and implementation details. The most authoritative source is reading the actual implementations, e.g. CPython's or PyPy's source code. – MisterMiyagi Aug 12 '20 at 13:18