[Python-checkins] CVS: python/nondist/peps pep-0253.txt,1.11,1.12
Guido van Rossum
gvanrossum@users.sourceforge.net
Wed, 11 Jul 2001 14:26:10 -0700
Update of /cvsroot/python/python/nondist/peps
In directory usw-pr-cvs1:/tmp/cvs-serv3385
Modified Files:
pep-0253.txt
Log Message:
Exhausted myself today by adding a long section on the new MRO.
Index: pep-0253.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0253.txt,v
retrieving revision 1.11
retrieving revision 1.12
diff -C2 -r1.11 -r1.12
*** pep-0253.txt 2001/07/11 19:09:28 1.11
--- pep-0253.txt 2001/07/11 21:26:08 1.12
***************
*** 142,149 ****
metatypes are typically written in C, and may be shared between
many regular types. (It will be possible to subtype metatypes in
! Python, so it won't be absolutely necessary to write C in order to
! use metatypes; but the power of Python metatypes will be limited.
! For example, Python code will never be allowed to allocate raw
! memory and initialize it at will.)
Metatypes determine various *policies* for types,such as what
--- 142,149 ----
metatypes are typically written in C, and may be shared between
many regular types. (It will be possible to subtype metatypes in
! Python, so it won't be absolutely necessary to write C to use
! metatypes; but the power of Python metatypes will be limited. For
! example, Python code will never be allowed to allocate raw memory
! and initialize it at will.)
Metatypes determine various *policies* for types,such as what
***************
*** 383,390 ****
for the tp_clear() slot. This turned out to be a bad idea.)
! In order to be usefully subtyped in C, a type must export the
! structure declaration for its instances through a header file, as
! it is needed in order to derive a subtype. The type object for
! the base type must also be exported.
If the base type has a type-checking macro (like PyDict_Check()),
--- 383,390 ----
for the tp_clear() slot. This turned out to be a bad idea.)
! To be usefully subtyped in C, a type must export the structure
! declaration for its instances through a header file, as it is
! needed to derive a subtype. The type object for the base type
! must also be exported.
If the base type has a type-checking macro (like PyDict_Check()),
***************
*** 482,490 ****
tp_new() and tp_dealloc() slots, respectively.
! In order to complete the initialization of the type,
! PyType_InitDict() must be called. This replaces slots initialized
! to zero in the subtype with the value of the corresponding base
! type slots. (It also fills in tp_dict, the type's dictionary, and
! does various other initializations necessary for type objects.)
A subtype is not usable until PyType_InitDict() is called for it;
--- 482,490 ----
tp_new() and tp_dealloc() slots, respectively.
! To complete the initialization of the type, PyType_InitDict() must
! be called. This replaces slots initialized to zero in the subtype
! with the value of the corresponding base type slots. (It also
! fills in tp_dict, the type's dictionary, and does various other
! initializations necessary for type objects.)
A subtype is not usable until PyType_InitDict() is called for it;
***************
*** 494,499 ****
to initialize the subtype in their constructor function. It is
allowed to call PyType_InitDict() more than once; the second and
! further calls have no effect. In order to avoid unnecessary
! calls, a test for tp_dict==NULL can be made.
(During initialization of the Python interpreter, some types are
--- 494,499 ----
to initialize the subtype in their constructor function. It is
allowed to call PyType_InitDict() more than once; the second and
! further calls have no effect. To avoid unnecessary calls, a test
! for tp_dict==NULL can be made.
(During initialization of the Python interpreter, some types are
***************
*** 638,642 ****
! Multiple Inheritance
The Python class statement supports multiple inheritance, and we
--- 638,642 ----
! Multiple inheritance
The Python class statement supports multiple inheritance, and we
***************
*** 726,733 ****
--- 726,882 ----
+ Method resolution order (the lookup rule)
+
+ With multiple inheritance comes the question of method resolution
+ order: the order in which a class or type and its bases are
+ searched looking for a method of a given name.
+
+ In classic Python, the rule is given by the following recursive
+ function, also known as the left-to-right depth-first rule:
+
+ def classic_lookup(cls, name):
+ if cls.__dict__.has_key(name):
+ return cls.__dict__[name]
+ for base in cls.__bases__:
+ try:
+ return classic_lookup(base, name)
+ except AttributeError:
+ pass
+ raise AttributeError, name
+
+ The problem with this becomes apparent when we consider a "diamond
+ diagram":
+
+ class A:
+ ^ ^ def save(self): ...
+ / \
+ / \
+ / \
+ / \
+ class B class C:
+ ^ ^ def save(self): ...
+ \ /
+ \ /
+ \ /
+ \ /
+ class D
+
+ Arrows point from a subtype to its base type(s). This particular
+ diagram means B and C derive from A, and D derives from B and C
+ (and hence also, indirectly, from A).
+
+ Assume that C overrides the method save(), which is defined in the
+ base A. (C.save() probably calls A.save() and then saves some of
+ its own state.) B and D don't override save(). When we invoke
+ save() on a D instance, which method is called? According to the
+ classic lookup rule, A.save() is called, ignoring C.save()!
+
+ This is not good. It probably breaks C (its state doesn't get
+ saved), defeating the whole purpose of inheriting from C in the
+ first place.
+
+ Why was this not a problem in classic Python? Diamond diagrams is
+ found rarely in classic Python class hierarchies. Most class
+ hierarchies use single inheritance, and multiple inheritance is
+ usually confined to mix-in classes. In fact, the problem shown
+ here is probably the reason why multiple inheritance is impopular
+ in classic Python.
+
+ Why will this be a problem in the new system? The 'object' type
+ at the top of the type hierarchy defines a number of methods that
+ can usefully be extended by subtypes, for example __getattr__().
+
+ (Aside: in classic Python, the __getattr__() method is not really
+ the implementation for the get-attribute operation; it is a hook
+ that only gets invoked when an attribute cannot be found by normal
+ means. This has often been cited as a shortcoming -- some class
+ designs have a legitimate need for a __getattr__() method that
+ gets called for *all* attribute references. But then of course
+ this method has to be able to invoke the default implementation
+ directly. The most natural way is to make the default
+ implementation available as object.__getattr__(self, name).)
+
+ Thus, a classic class hierarchy like this:
+
+ class B class C:
+ ^ ^ def __getattr__(self, name): ...
+ \ /
+ \ /
+ \ /
+ \ /
+ class D
+
+ will change into a diamond diagram under the new system:
+
+ object:
+ ^ ^ __getattr__()
+ / \
+ / \
+ / \
+ / \
+ class B class C:
+ ^ ^ def __getattr__(self, name): ...
+ \ /
+ \ /
+ \ /
+ \ /
+ class D
+
+ and while in the original diagram C.__getattr__() is invoked,
+ under the new system with the classic lookup rule,
+ object.__getattr__() would be invoked!
+
+ Fortunately, there's a lookup rule that's better. It's a bit
+ difficult to explain, but it does the right thing in the diamond
+ diagram, and it is the same as the classic lookup rule when there
+ are no diamonds in the inheritance graph (when it is a tree).
+
+ The new lookup rule constructs a list of all classes in the
+ inheritance diagram in the order in which they will be searched.
+ This construction is done at class definition time to save time.
+ To explain the new lookup rule, let's first consider what such a
+ list would look like for the classic lookup rule. Note that in
+ the presence of diamonds the classic lookup visits some classes
+ multiple times. For example, in the ABCD diamond diagram above,
+ the classic lookup rule visits the classes in this order:
+
+ D, B, A, C, A
+
+ Note how A occurs twice in the list. The second occurrence is
+ redundant, since anything that could be found there would already
+ have been found when searching the first occurrence.
+
+ We use this observation to explain our new lookup rule. Using the
+ classic lookup rule, construct the list of classes that would be
+ searched, including duplicates. Now for each class that occurs in
+ the list multiple times, remove all occurrences except for the
+ last. The resulting list contains each ancestor class exactly
+ once (including the most derived class, D in the example).
+
+ Searching for methods in this order will do the right thing for
+ the diamond diagram. Because of the way the list is constructed,
+ it does not change the search order in situations where no diamond
+ is involved.
+
+ Isn't this backwards incompatible? Won't it break existing code?
+ It would, if we changed the method resolution order for all
+ classes. However, in Python 2.2, the new lookup rule will only be
+ applied to types derived from built-in types, which is a new
+ feature. Class statements without a base class create "classic
+ classes", and so do class statements whose base classes are
+ themselves classic classes. For classic classes the classic
+ lookup rule will be used. (To experiment with the new lookup rule
+ for classic classes, you will be able to specify a different
+ metaclass explicitly.) We'll also provide a tool that analyzes a
+ class hierarchy looking for methods that would be affected by a
+ change in method resolution order.
+
+
XXX To be done
Additional topics to be discussed in this PEP:
+ - backwards compatibility issues!!!
+
- class methods and static methods
***************
*** 737,742 ****
- built-in names for built-in types (object, int, str, list etc.)
- - method resolution order
-
- __dict__
--- 886,889 ----
***************
*** 748,751 ****
--- 895,902 ----
- API docs for all the new functions
+
+ - using __new__
+
+ - writing metaclasses (using mro() etc.)
- high level user overview