Is there a compelling reason to call type.mro()
rather than iterate over type.__mro__
directly? It's literally ~13 times faster to access (36ns vs 488 ns)
I stumbled upon it while looking to cache type.mro()
. It seems legit, but it makes me wonder: can I rely on type.__mro__
, or do I have to call type.mro()
? and under what conditions can I get away with the former?
More importantly, what set of conditions would have to occur for type.__mro__
to be invalid?
For instance, when a new subclass is defined/created that alters an existing class's mro, is the existing class' .__mro__
immediately updated? Does this happen on every new class creation? that makes it part of class type? Which part? ..or is that what type.mro()
is about?
Of course, all that is assuming that type.__mro__
is, in fact, a tuple of cached names pointing to the objects in a given type's mro. If that assumption is incorrect; then, what is it? (probably a descriptor or something..) and why can/can't I use it?
EDIT: If it is a descriptor, then I'd love to learn its magic, as both: type(type.__mro__) is tuple
and type(type(type).__mro__) is tuple
(ie: probably not a descriptor)
EDIT: Not sure how relevant this is, but type('whatever').mro()
returns a list whereas type('whatever').__mro__
returns a tuple. (Un?)fortunately, appending to that list doesn't change the __mro__
or subsequent calls to .mro()
of/on the type in question (in this case, str).
Thanks for the help!
According to the docs:
class.__mro__
This attribute is a tuple of classes that are considered when looking for base classes during method resolution.
class.mro()
This method can be overridden by a metaclass to customize the method resolution order for its instances. It is called at class instantiation, and its result is stored in
__mro__
.
So yes, your assumption about __mro__
being a cache is correct. If your metaclass' mro()
always returns the same thing, or if you don't have any metaclasses, you can safely use __mro__
.