[Python-checkins] peps: Update 422 based on python-dev feedback

nick.coghlan python-checkins at python.org
Thu Jun 7 14:08:54 CEST 2012


http://hg.python.org/peps/rev/d65ae0ea8b46
changeset:   4456:d65ae0ea8b46
user:        Nick Coghlan <ncoghlan at gmail.com>
date:        Thu Jun 07 22:08:41 2012 +1000
summary:
  Update 422 based on python-dev feedback

files:
  pep-0422.txt |  97 ++++++++++++++++++++++++++++++---------
  1 files changed, 73 insertions(+), 24 deletions(-)


diff --git a/pep-0422.txt b/pep-0422.txt
--- a/pep-0422.txt
+++ b/pep-0422.txt
@@ -44,7 +44,7 @@
 While in many cases these two meanings end up referring to one and the same
 object, there are two situations where that is not the case:
 
-* If the metaclass hint refers to an instance of ``type``, then it is
+* If the metaclass hint refers to a subclass of ``type``, then it is
   considered as a candidate metaclass along with the metaclasses of all of
   the parents of the class being defined. If a more appropriate metaclass is
   found amongst the candidates, then it will be used instead of the one
@@ -72,6 +72,16 @@
 class namespace to use as the metaclass hint. There is `published code`_ that
 makes use of this feature.
 
+Another new feature in Python 3 is the zero-argument form of the ``super()``
+builtin, introduced by PEP 3135. This feature uses an implicit ``__class__``
+reference to the class being defined to replace the "by name" references
+required in Python 2. Just as code invoked during execution of a Python 2
+metaclass could not call methods that referenced the class by name (as the
+name had not yet been bound in the containing scope), similarly, Python 3
+metaclasses cannot call methods that rely on the implicit ``__class__``
+reference (as it is not populated until after the metaclass has returned
+control to the class creation machiner).
+
 
 Proposal
 ========
@@ -90,10 +100,10 @@
 4. Can be added to an existing base class without a significant risk of
    introducing backwards compatibility problems
 
-One mechanism that would achieve this goal is to add a new class
+One mechanism that can achieve this goal is to add a new class
 initialisation hook, modelled directly on the existing instance
-initialisation hook. However, the signature would be constrained to ensure
-that correctly supporting multiple inheritance is kept as simple as possible.
+initialisation hook, but with the signature constrained to match that
+of an ordinary class decorator.
 
 Specifically, it is proposed that class definitions be able to provide a
 class initialisation hook as follows::
@@ -110,51 +120,57 @@
 If present on the created object, this new hook will be called by the class
 creation machinery *after* the ``__class__`` reference has been initialised.
 For ``types.new_class()``, it will be called as the last step before
-returning the created class object. Calling the hook automatically from
-``type.__init__`` unfortunately doesn't work, as it would mean the
-``__init_class__`` method would be unable to call any methods that relied
-on the ``__class__`` reference (or used the zero-argument form of
-``super()``).
+returning the created class object.
 
 If a metaclass wishes to block class initialisation for some reason, it
 must arrange for ``cls.__init_class__`` to trigger ``AttributeError``.
 
-This general proposal is not a new idea (it was first suggested `more than
-10 years ago`_), but I believe the situation has changed sufficiently in
-that time that the idea is worth reconsidering.
+This general proposal is not a new idea (it was first suggested for
+inclusion in the language definition `more than 10 years ago`_, and a
+similar mechanism has long been supported by `Zope's ExtensionClass`_),
+but I believe the situation has changed sufficiently in recent years that
+the idea is worth reconsidering.
 
 
 Key Benefits
 ============
 
 
-Replaces dynamic setting of ``__metaclass__``
----------------------------------------------
+Replaces many use cases for dynamic setting of ``__metaclass__``
+-----------------------------------------------------------------
 
-For use cases that didn't involve completely replacing the defined class,
+For use cases that don't involve completely replacing the defined class,
 Python 2 code that dynamically set ``__metaclass__`` can now dynamically
 set ``__init_class__`` instead. For more advanced use cases, introduction of
-an explicit metaclass will still be necessary in order to support Python 3.
+an explicit metaclass (possibly made available as a required base class) will
+still be necessary in order to support Python 3.
 
 
 Easier inheritance of definition time behaviour
 -----------------------------------------------
 
-Understanding Python's metaclass system requires a deep understanding of
+Understanding Python's metaclasses requires a deep understanding of
 the type system and the class construction process. This is legitimately
-seen as confusing, due to the need to keep multiple moving parts (the code,
+seen as challenging, due to the need to keep multiple moving parts (the code,
 the metaclass hint, the actual metaclass, the class object, instances of the
-class object) clearly distinct in your mind.
+class object) clearly distinct in your mind. Even when you know the rules,
+it's still easy to make a mistake if you're not being extremely careful.
+An earlier version of this PEP actually included such a mistake: it
+stated "instance of type" for a constraint that is actually "subclass of
+type".
 
-Understanding the proposed class initialisation hook requires understanding
-decorators and ordinary method inheritance, which is a much simpler prospect.
+Understanding the proposed class initialisation hook only requires
+understanding decorators and ordinary method inheritance, which isn't
+quite as daunting a task. The new hook provides a more gradual path
+towards understanding all of the phases involved in the class definition
+process.
 
 
 Reduced chance of metaclass conflicts
 -------------------------------------
 
 One of the big issues that makes library authors reluctant to use metaclasses
-(even when it would be appropriate) is the risk of metaclass conflicts.
+(even when they would be appropriate) is the risk of metaclass conflicts.
 These occur whenever two unrelated metaclasses are used by the desired
 parents of a class definition. This risk also makes it very difficult to
 *add* a metaclass to a class that has previously been published without one.
@@ -164,12 +180,12 @@
 is a risk of breaking poorly implemented subclasses, but when that occurs,
 it is recognised as a bug in the subclass rather than the library author
 breaching backwards compatibility guarantees. In fact, due to the constrained
-signature, the risk in this case is actually even lower than in the case of
-``__init__``.
+signature of ``__init_class__``, the risk in this case is actually even
+lower than in the case of ``__init__``.
 
 
-Integrates cleanly with PEP 3135
---------------------------------
+Integrates cleanly with \PEP 3135
+---------------------------------
 
 Unlike code that runs as part of the metaclass, code that runs as part of
 the new hook will be able to freely invoke class methods that rely on the
@@ -280,6 +296,35 @@
 ordinary class method invocation.
 
 
+Automatic metaclass derivation
+------------------------------
+
+When no appropriate metaclass is found, it's theoretically possible to
+automatically derive a metaclass for a new type based on the metaclass hint
+and the metaclasses of the bases.
+
+While adding such a mechanism would reduce the risk of spurious metaclass
+conflicts, it would do nothing to improve integration with PEP 3135, would
+not help with porting Python 2 code that set ``__metaclass__`` dynamically
+and would not provide a more straightforward inherited mechanism for invoking
+additional operations after the class invocation is complete.
+
+In addition, there would still be a risk of metaclass conflicts in cases
+where the base metaclasses were not written with multiple inheritance in
+mind. In such situations, there's a chance of introducing latent defects
+if one or more metaclasses are not invoked correctly.
+
+
+Calling the new hook from ``type.__init__``
+-------------------------------------------
+
+Calling the new hook automatically from ``type.__init__``, would achieve most
+of the goals of this PEP. However, using that approach would mean that
+``__init_class__`` implementations would be unable to call any methods that
+relied on the ``__class__`` reference (or used the zero-argument form of
+``super()``), and could not make use of those features themselves.
+
+
 References
 ==========
 
@@ -289,6 +334,8 @@
 .. _more than 10 years ago:
    http://mail.python.org/pipermail/python-dev/2001-November/018651.html
 
+.. _Zope's ExtensionClass:
+   http://docs.zope.org/zope_secrets/extensionclass.html
 
 Copyright
 =========

-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list