They're not very different, and the description from the book covers the two ways in which they differ:
- Descriptors found on a class instance (after not being found on the class) don't get invoked (
a.x = somedescriptor()
where a
is a class instance, not a class, followed by a.x
will just return the instance of somedescriptor()
you just made), while descriptors found on a metaclass instance i.e. a class (after not being found on the metaclass) get invoked with None
as the instance it was called on (A.x = somedescriptor()
where A
is a metaclass instance, not a metaclass, will invoke .__get__(None, A)
on the somedescriptor()
you just made). This allows stuff like @classmethod
to work by binding the method to the class itself whether it's looked up on an instance of the class or the class itself.
- Class instances don't have a concept of "parent instances" (the namespace of the class instance itself is a flat
dict
, even if the attributes associated with that class instance were defined by methods from multiple levels of inheritance), so the idea of MRO-based lookup is unique to metaclass instances.
Everything else is pretty much the same, it's just that the book is glossing over the concept of metaclasses here, since most classes are instances of the base type
, which has no special behaviors. If you have a metaclass other than type
, the full instance lookup rules apply when looking up attributes on a class (it's just the class of the class is the metaclass).
They were probably trying to avoid the complexity of metaclasses early on, but in the process made it seem like the rules for instance lookup didn't apply to classes; they do, it's just that classes add a couple extra rules to the basic lookup procedure.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…