[Python-checkins] r55276 - peps/trunk/pep-3119.txt

guido.van.rossum python-checkins at python.org
Fri May 11 22:49:16 CEST 2007


Author: guido.van.rossum
Date: Fri May 11 22:49:12 2007
New Revision: 55276

Modified:
   peps/trunk/pep-3119.txt
Log:
Checkpoint.  Rewrote (and swapped) the sections on
isinstance/issubclass overriding and the support framework.
Next up: redesign the collection ABCs.



Modified: peps/trunk/pep-3119.txt
==============================================================================
--- peps/trunk/pep-3119.txt	(original)
+++ peps/trunk/pep-3119.txt	Fri May 11 22:49:12 2007
@@ -16,12 +16,12 @@
 This is a proposal to add Abstract Base Class (ABC) support to Python
 3000.  It proposes:
 
-* An "ABC support framework" which defines a built-in decorator that
-  can be used to define abstract methods.  A class containing an
-  abstract method that isn't overridden cannot be instantiated.
-
 * A way to overload ``isinstance()`` and ``issubclass()``.
 
+* A new module ``abc`` which serves as an "ABC support framework".  It
+  defines a metaclass for use with ABCs and a decorator that can be
+  used to define abstract methods.
+
 * Specific ABCs for containers and iterators, to be added to the
   collections module.
 
@@ -100,8 +100,8 @@
 known as Abstract Base Classes, or ABC.  ABCs are simply Python
 classes that are added into an object's inheritance tree to signal
 certain features of that object to an external inspector.  Tests are
-done using isinstance(), and the presence of a particular ABC means
-that the test has passed.
+done using ``isinstance()``, and the presence of a particular ABC
+means that the test has passed.
 
 In addition, the ABCs define a minimal set of methods that establish
 the characteristic behavior of the type.  Code that discriminates
@@ -123,72 +123,16 @@
 
 The specification follows the categories listed in the abstract:
 
-* An "ABC support framework" which defines a built-in decorator that
-  make it easy to define ABCs, and mechanisms to support it.
-
 * A way to overload ``isinstance()`` and ``issubclass()``.
 
+* A new module ``abc`` which serves as an "ABC support framework".  It
+  defines a metaclass for use with ABCs and a decorator that can be
+  used to define abstract methods.
+
 * Specific ABCs for containers and iterators, to be added to the
   collections module.
 
 
-ABC Support Framework
----------------------
-
-We define a new built-in decorator, ``@abstractmethod``, to be used to
-declare abstract methods.  A class containing at least one method
-declared with this decorator that hasn't been overridden yet cannot be
-instantiated.  Such a methods may be called from the overriding method
-in the subclass (using ``super`` or direct invocation).  For example::
-
-    class A:
-        @abstractmethod
-        def foo(self): pass
-
-    A()  # raises TypeError
-
-    class B(A):
-        pass
-
-    B()  # raises TypeError
-
-    class C(A):
-        def foo(self): print(42)
-
-    C()  # works
-
-**Note:** The ``@abstractmethod`` decorator should only be used inside
-a class body.  Dynamically adding abstract methods to a class, or
-attempting to modify the abstraction status of a method or class once
-it is created, are not supported.
-
-**Implementation:** The ``@abstractmethod`` decorator sets the
-function attribute ``__isabstractmethod__`` to the value ``True``.
-The ``type.__new__`` method computes the type attribute
-``__abstractmethods__`` as the set of all method names that have an
-``__isabstractmethod__`` attribute whose value is true.  It does this
-by combining the ``__abstractmethods__`` attributes of the base
-classes, adding the names of all methods in the new class dict that
-have a true ``__isabstractmethod__`` attribute, and removing the names
-of all methods in the new class dict that don't have a true
-``__isabstractmethod__`` attribute.  If the resulting
-``__abstractmethods__`` set is non-empty, the class is considered
-abstract, and attempts to instantiate it will raise ``TypeError``.
-(CPython can uses an internal flag ``Py_TPFLAGS_ABSTRACT`` to speed up
-this check [6]_.)
-
-**Discussion:** Unlike C++ or Java, abstract methods as defined here
-may have an implementation.  This implementation can be called via the
-``super`` mechanism from the class that overrides it.  This could be
-useful as an end-point for a super-call in framework using a
-cooperative multiple-inheritance [7]_, [8]_.
-
-**Open issues:** Should we also provide a standard way to declare
-abstract data attributes?  If so, how should these be spelled?
-Perhaps place ``@abstractattribute`` decorators on properties?  Or use
-an ``@attributes(name1, name2, ...)`` class decorator?
-
-
 Overloading ``isinstance()`` and ``issubclass()``
 -------------------------------------------------
 
@@ -199,10 +143,10 @@
 numbers: MonoidUnderPlus, AdditiveGroup, Ring, Field, Complex (each
 derived from the previous).  And the discussion mentioned several
 other algebraic categorizations that were left out: Algebraic,
-Transcendental, and IntegralDomain, and PrincipalIdealDomain.  In this
-PEP, we are wondering about the use cases for separate classes like
-Set, ComposableSet, MutableSet, HashableSet, MutableComposableSet,
-HashableComposableSet.
+Transcendental, and IntegralDomain, and PrincipalIdealDomain.  In
+earlier versions of the current PEP, we considered the use cases for
+separate classes like Set, ComposableSet, MutableSet, HashableSet,
+MutableComposableSet, HashableComposableSet.
 
 The dilemma here is that we'd rather have fewer ABCs, but then what
 should a user do who needs a less refined ABC?  Consider e.g. the
@@ -214,10 +158,33 @@
 interpreters running in the same address space, as is used by
 mod_python).
 
-The solution proposed here is to allow overloading the built-in
-functions ``isinstance()`` and ``issubclass()``.  The overloading
-works as follows: The call ``isinstance(x, C)`` first checks whether
-``C.__instancecheck__`` exists, and if so, calls
+Another example would be someone who wants to define a generic
+function (PEP 3124) for any sequences that has an ``append()`` method.
+The ``Sequence`` ABC (see below) doesn't promise the ``append()``
+method, while ``MutableSequence`` requires not only ``append()`` but
+also various other mutating methods.
+
+To solve these and similar dilemmas, the next section will propose a
+metaclass for use with ABCs that will allow us to add an ABC as a
+"virtual base class" (not the same concept as in C++) to any class,
+including to another ABC.  This allows the standard library to define
+ABCs ``Sequence`` and ``MutableSequence`` and register these as
+virtual base classes for built-in types like ``basestring``, ``tuple``
+and ``list``, so that for example the following conditions are all
+true::
+
+    isinstance([], Sequence)
+    issubclass(list, Sequence)
+    issubclass(list, MutableSequence)
+    isinstance((), Sequence)
+    not issubclass(tuple, MutableSequence)
+    isinstance("", Sequence)
+    issubclass(bytes, MutableSequence)
+
+The primary mechanism proposed here is to allow overloading the
+built-in functions ``isinstance()`` and ``issubclass()``.  The
+overloading works as follows: The call ``isinstance(x, C)`` first
+checks whether ``C.__instancecheck__`` exists, and if so, calls
 ``C.__instancecheck__(x)`` instead of its normal implementation.
 Similarly, the call ``issubclass(D, C)`` first checks whether
 ``C.__subclasscheck__`` exists, and if so, calls
@@ -227,20 +194,12 @@
 ``__issubclass__``; this is because the reversal of the arguments
 could cause confusion, especially for the ``issubclass()`` overloader.
 
-(We could also provide a default implementation of these that
-implements the old algorithms; this would be more regular but would
-have additional backwards compatibility issues, since the old
-algorithms special-case objects not deriving from ``type`` in order to
-support a different kind of overloading of these operations.)
-
 A prototype implementation of this is given in [12]_.
 
-Here is an example with (very simple) implementations of
+Here is an example with (naively simple) implementations of
 ``__instancecheck__`` and ``__subclasscheck__``::
 
-    assert issubclass(set, HashableSet)  # Assume this is given
-
-    class ABC:
+    class ABCMeta(type):
 
         def __instancecheck__(cls, inst):
             """Implement isinstance(inst, cls)."""
@@ -252,11 +211,158 @@
             candidates = cls.__dict__.get("__subclass__", set()) | {cls}
             return any(c in candidates for c in sub.mro())
 
-    class NoncomposableHashableSet(Set, Hashable, metaclass=ABC):
-        __subclass__ = {HashableSet}
+    class Sequence(metaclass=ABCMeta):
+        __subclass__ = {list, tuple}
+
+    assert issubclass(list, Sequence)
+    assert issubclass(tuple, Sequence)
+
+    class AppendableSequence(Sequence):
+        __subclass__ = {list}
+
+    assert issubclass(list, AppendableSequence)
+    assert isinstance([], AppendableSequence)
+
+    assert not issubclass(tuple, AppendableSequence)
+    assert not isinstance((), AppendableSequence)
+
+The next section proposes a full-fledged implementation.
+
+
+The ``abc`` Module: an ABC Support Framework
+--------------------------------------------
+
+The new standard library module ``abc``, written in pure Python,
+serves as an ABC support framework.  It defines a metaclass
+``ABCMeta`` and a decorator ``@abstractmethod``.  A sample
+implementation is given by [13]_.
+
+The ``ABCMeta`` class overrides ``__instancecheck__`` and
+``__subclasscheck__`` and defines a ``register`` method.  The
+``register`` method takes one argument, which much be a class; after
+the call ``B.register(C)``, the call ``issubclass(C, B)`` will return
+True, by virtue of of ``B.__subclasscheck__(C)`` returning True.
+Also, ``isinstance(x, B)`` is equivalent to ``issubclass(x.__class__,
+B) or issubclass(type(x), B)``.  (It is possible ``type(x)`` and
+``x.__class__`` are not the same object, e.g. when x is a proxy
+object.)
+
+These methods are intended to be be called on classes whose metaclass
+is (derived from) ``ABCMeta``; for example::
+
+    from abc import ABCMeta
+
+    class MyABC(metaclass=ABCMeta):
+        pass
+
+    MyABC.register(tuple)
 
-    assert issubclass(set, NoncomposableHashableSet)
-    assert isinstance({1, 2, 3}, NoncomposableHashableSet)
+    assert issubclass(tuple, MyABC)
+    assert isinstance((), MyABC)
+
+The last two asserts are equivalent to the following two::
+
+    assert MyABC.__subclasscheck__(tuple)
+    assert MyABC.__instancecheck__(())
+
+Of course, you can also directly subclass MyABC::
+
+    class MyClass(MyABC):
+        pass
+
+    assert issubclass(MyClass, MyABC)
+    assert isinstance(MyClass(), MyABC)
+
+Also, of course, a tuple is not a ``MyClass``::
+
+    assert not issubclass(tuple, MyClass)
+    assert not isinstance((), MyClass)
+
+You can register another class as a subclass of ``MyClass``::
+
+    MyClass.register(list)
+
+    assert issubclass(list, MyClass)
+    assert issubclass(list, MyABC)
+
+You can also register another ABC::
+
+    class AnotherClass(metaclass=ABCMeta):
+        pass
+
+    AnotherClass.register(basestring)
+
+    MyClass.register(AnotherClass)
+
+    assert isinstance(str, MyABC)
+
+That last assert requires tracing the following superclass-subclass
+relationships::
+
+    MyABC -> MyClass (using regular subclassing)
+    MyClass -> AnotherClass (using registration)
+    AnotherClass -> basestring (using registration)
+    basestring -> str (using regular subclassing)
+
+The ``abc`` module also defines a new decorator, ``@abstractmethod``,
+to be used to declare abstract methods.  A class containing at least
+one method declared with this decorator that hasn't been overridden
+yet cannot be instantiated.  Such a methods may be called from the
+overriding method in the subclass (using ``super`` or direct
+invocation).  For example::
+
+    from abc import ABCMeta, abstractmethod
+
+    class A(metaclass=ABCMeta):
+        @abstractmethod
+        def foo(self): pass
+
+    A()  # raises TypeError
+
+    class B(A):
+        pass
+
+    B()  # raises TypeError
+
+    class C(A):
+        def foo(self): print(42)
+
+    C()  # works
+
+**Notes:** The ``@abstractmethod`` decorator should only be used
+inside a class body, and only for classes whose metaclass is (derived
+from) ``ABCMeta``.  Dynamically adding abstract methods to a class, or
+attempting to modify the abstraction status of a method or class once
+it is created, are not supported.  The ``@abstractmethod`` only
+affects subclasses derived using regular inheritance; "virtual
+subclasses" registered with the ``register()`` method are not affected.
+
+**Implementation:** The ``@abstractmethod`` decorator sets the
+function attribute ``__isabstractmethod__`` to the value ``True``.
+The ``ABCMeta.__new__`` method computes the type attribute
+``__abstractmethods__`` as the set of all method names that have an
+``__isabstractmethod__`` attribute whose value is true.  It does this
+by combining the ``__abstractmethods__`` attributes of the base
+classes, adding the names of all methods in the new class dict that
+have a true ``__isabstractmethod__`` attribute, and removing the names
+of all methods in the new class dict that don't have a true
+``__isabstractmethod__`` attribute.  If the resulting
+``__abstractmethods__`` set is non-empty, the class is considered
+abstract, and attempts to instantiate it will raise ``TypeError``.
+(If this were implemented in CPython, an internal flag
+``Py_TPFLAGS_ABSTRACT`` could be used to speed up this check [6]_.)
+
+**Discussion:** Unlike C++ or Java, abstract methods as defined here
+may have an implementation.  This implementation can be called via the
+``super`` mechanism from the class that overrides it.  This could be
+useful as an end-point for a super-call in framework using a
+cooperative multiple-inheritance [7]_, [8]_.
+
+**Open issues:** Should we also provide a standard way to declare
+abstract data attributes?  If so, how should these be spelled?
+Perhaps place ``@abstractattribute`` decorators on properties?  Or use
+an ``@attributes(name1, name2, ...)`` class decorator?  Strawman:
+let's back off on this; it's easy enough to add this later.
 
 
 ABCs for Containers and Iterators
@@ -831,6 +937,9 @@
 .. [12] Make isinstance/issubclass overloadable
    (http://python.org/sf/1708353)
 
+.. [13] ABCMeta sample implementation
+   (http://svn.python.org/view/sandbox/trunk/abc/xyz.py)
+
 
 Copyright
 =========


More information about the Python-checkins mailing list