From 4fca3251eeece2d1ed6690f591a31b12cbb3caa6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 19 Jul 2025 17:33:05 -0700 Subject: [PATCH 1/7] gh-136843: Document how multiple inheritance works --- Doc/reference/compound_stmts.rst | 94 ++++++++++++++++++++++++++++++++ Doc/reference/datamodel.rst | 2 + Doc/tutorial/classes.rst | 3 + 3 files changed, 99 insertions(+) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index e95fa3a6424e23..2eef548927c67e 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1421,6 +1421,9 @@ is equivalent to :: class Foo(object): pass +There may be one or more base classes; see :ref:`multiple-inheritance` below for more +information. + The class's suite is then executed in a new execution frame (see :ref:`naming`), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class's @@ -1490,6 +1493,97 @@ can be used to create instance variables with different implementation details. were introduced in :pep:`318`. +.. _multiple-inheritance: + +Multiple inheritance +-------------------- + +Python classes may have multiple base classes, a technique known as +*multiple inheritance*. The base classes are specified in the class definition +by listing them in parentheses after the class name, separated by commas. +For example, the following class definition:: + + class C(A, B): + pass + +defines a class ``C`` that inherits from classes ``A`` and ``B``. + +The :term:`method resolution order` (MRO) is the order in which base classes are +searched when looking up an attribute on a class. See :ref:`python_2.3_mro` for a +description of how Python determines the MRO for a class. + +Multiple inheritance is not always allowed. Attempting to define a class with multiple +inheritance will raise an error if one of the bases is invalid, if a consistent MRO +cannot be created, if no valid metaclass can be determined, or if there is an instance +layout conflict. We'll discuss each of these in turn. + +First, all base classes must allow subclassing. While most classes allow subclassing, +some built-in classes do not, such as :class:`bool`:: + + class SubBool(bool): # TypeError + pass + +To create a consistent MRO, all bases appear in the order +they were specified in the base class list and every child class must appear before its +base classes. Below is an example where this fails:: + + class Base: pass + class Child(Base): pass + class Grandchild(Base, Child): pass # TypeError + +In the MRO of ``Grandchild``, ``Child`` must appear before ``Base`` because it is first +in the base class list, but it must also appear after ``Base`` because it is a child of +``Base``. This is a contradiction, so the class cannot be defined. + +If some of the bases have a custom :term:`metaclass`, the metaclass of the resulting class +is chosen among the metaclasses of the bases. It must be a metaclass that is a subclass of +all other candidate metaclasses. If no such metaclass exists, the class cannot be created, +as explained in :ref:`metaclass-determination`. + +Finally, the memory layouts of the bases must be compatible. This means that it must be +possible to compute a *solid base* for the class. A class is a solid base if it has a +nonempty :attr:`~object.__slots__` definition; some other classes may also be solid bases, +depending on the Python implementation. + +.. impl-detail:: + + In CPython, many but not all classes defined in C are solid bases, including most + builtins but excluding most concrete :class:`Exception` classes. Generally, a C class + is a solid base if its underlying struct is different in size from its base class. + +Every class has a solid base. :class:`object`, the base class, has itself as its solid base. +If there is a single base, the child class's solid base is that class if it is a solid baes, +or else the base class's solid base. If there are multiple bases, we first find the solid base +for each base class to produce a list of candidate solid bases. If there is a unique solid base +that is a subclass of all others, then that class is the solid base. Otherwise, class creation +fails. + +Example:: + + class Solid1: + __slots__ = ("solid1",) + + class Solid2: + __slots__ = ("solid2",) + + class SolidChild(Solid1): + __slots__ = ("solid_child",) + + class C1: # solid base is `object` + pass + + # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + class C2(Solid1, C1): # solid base is `Solid1` + pass + + # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + class C3(SolidChild, Solid1): # solid base is `SolidChild` + pass + + # Error: solid bases are `Solid1` and `Solid2`, but they are not subclasses of each other. + class C4(Solid1, Solid2): # error: no single solid base + pass + .. _async: Coroutines diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 4a099e81daccb3..215cde6b6834ec 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2779,6 +2779,8 @@ Resolving MRO entries Core support for typing module and generic types. +.. _metaclass-determination: + Determining the appropriate metaclass ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. index:: diff --git a/Doc/tutorial/classes.rst b/Doc/tutorial/classes.rst index 9d0fab8861d2a9..fa964271d79bd8 100644 --- a/Doc/tutorial/classes.rst +++ b/Doc/tutorial/classes.rst @@ -663,6 +663,9 @@ Taken together, these properties make it possible to design reliable and extensible classes with multiple inheritance. For more detail, see :ref:`python_2.3_mro`. +In some cases multiple inheritance is not allowed; see :ref:`multiple-inheritance` +for details. + .. _tut-private: From ca07bc9f84297b09430ab35cf1ad9db405496d96 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Mon, 21 Jul 2025 06:41:39 -0700 Subject: [PATCH 2/7] doctests --- Doc/reference/compound_stmts.rst | 88 +++++++++++++++++++------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 2eef548927c67e..c7f4662cae5341 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1501,10 +1501,13 @@ Multiple inheritance Python classes may have multiple base classes, a technique known as *multiple inheritance*. The base classes are specified in the class definition by listing them in parentheses after the class name, separated by commas. -For example, the following class definition:: +For example, the following class definition: - class C(A, B): - pass +.. doctest:: + + >>> class A: pass + >>> class B: pass + >>> class C(A, B): pass defines a class ``C`` that inherits from classes ``A`` and ``B``. @@ -1518,18 +1521,28 @@ cannot be created, if no valid metaclass can be determined, or if there is an in layout conflict. We'll discuss each of these in turn. First, all base classes must allow subclassing. While most classes allow subclassing, -some built-in classes do not, such as :class:`bool`:: +some built-in classes do not, such as :class:`bool`: - class SubBool(bool): # TypeError - pass +.. doctest:: + + >>> class SubBool(bool): # TypeError + ... pass + Traceback (most recent call last): + ... + TypeError: type 'bool' is not an acceptable base type To create a consistent MRO, all bases appear in the order they were specified in the base class list and every child class must appear before its -base classes. Below is an example where this fails:: +base classes. Below is an example where this fails: + +.. doctest:: - class Base: pass - class Child(Base): pass - class Grandchild(Base, Child): pass # TypeError + >>> class Base: pass + >>> class Child(Base): pass + >>> class Grandchild(Base, Child): pass # TypeError + Traceback (most recent call last): + ... + TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child In the MRO of ``Grandchild``, ``Child`` must appear before ``Base`` because it is first in the base class list, but it must also appear after ``Base`` because it is a child of @@ -1558,31 +1571,36 @@ for each base class to produce a list of candidate solid bases. If there is a un that is a subclass of all others, then that class is the solid base. Otherwise, class creation fails. -Example:: - - class Solid1: - __slots__ = ("solid1",) - - class Solid2: - __slots__ = ("solid2",) - - class SolidChild(Solid1): - __slots__ = ("solid_child",) - - class C1: # solid base is `object` - pass - - # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. - class C2(Solid1, C1): # solid base is `Solid1` - pass - - # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. - class C3(SolidChild, Solid1): # solid base is `SolidChild` - pass - - # Error: solid bases are `Solid1` and `Solid2`, but they are not subclasses of each other. - class C4(Solid1, Solid2): # error: no single solid base - pass +Example: + +.. doctest:: + + >>> class Solid1: + ... __slots__ = ("solid1",) + >>> + >>> class Solid2: + ... __slots__ = ("solid2",) + >>> + >>> class SolidChild(Solid1): + ... __slots__ = ("solid_child",) + >>> + >>> class C1: # solid base is `object` + ... pass + >>> + >>> # OK: solid bases are `Solid1` and `object`, and `Solid1` is a subclass of `object`. + >>> class C2(Solid1, C1): # solid base is `Solid1` + ... pass + >>> + >>> # OK: solid bases are `SolidChild` and `Solid1`, and `SolidChild` is a subclass of `Solid1`. + >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` + ... pass + >>> + >>> # Error: solid bases are `Solid1` and `Solid2`, but they are not subclasses of each other. + >>> class C4(Solid1, Solid2): # error: no single solid base + ... pass + Traceback (most recent call last): + ... + TypeError: multiple bases have instance lay-out conflict .. _async: From bf5702bb64faeecb19aaa0d41dfd40509e1090f3 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 22 Jul 2025 07:04:33 -0700 Subject: [PATCH 3/7] Update Doc/reference/compound_stmts.rst Co-authored-by: Petr Viktorin --- Doc/reference/compound_stmts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index c7f4662cae5341..02451397ee8da6 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1565,7 +1565,7 @@ depending on the Python implementation. is a solid base if its underlying struct is different in size from its base class. Every class has a solid base. :class:`object`, the base class, has itself as its solid base. -If there is a single base, the child class's solid base is that class if it is a solid baes, +If there is a single base, the child class's solid base is that class if it is a solid base, or else the base class's solid base. If there are multiple bases, we first find the solid base for each base class to produce a list of candidate solid bases. If there is a unique solid base that is a subclass of all others, then that class is the solid base. Otherwise, class creation From 4e7913f1474c8cc98139758e4e03581c96acb3f2 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 22 Jul 2025 07:16:18 -0700 Subject: [PATCH 4/7] Feedback from Alex --- Doc/reference/compound_stmts.rst | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 02451397ee8da6..ae6f8ec79cf1e6 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1531,9 +1531,10 @@ some built-in classes do not, such as :class:`bool`: ... TypeError: type 'bool' is not an acceptable base type -To create a consistent MRO, all bases appear in the order -they were specified in the base class list and every child class must appear before its -base classes. Below is an example where this fails: +In the resolved MRO of a class, the class's bases appear in the order they were +specified in the class's bases list. Additionally, the MRO always lists a child +class before any of its bases. A class definition will fail if it is impossible to +resolve a consistent MRO that satisfies these rules from the list of bases provided: .. doctest:: @@ -1549,9 +1550,10 @@ in the base class list, but it must also appear after ``Base`` because it is a c ``Base``. This is a contradiction, so the class cannot be defined. If some of the bases have a custom :term:`metaclass`, the metaclass of the resulting class -is chosen among the metaclasses of the bases. It must be a metaclass that is a subclass of -all other candidate metaclasses. If no such metaclass exists, the class cannot be created, -as explained in :ref:`metaclass-determination`. +is chosen among the metaclasses of the bases and the explicitly specified metaclass of the +child class. It must be a metaclass that is a subclass of +all other candidate metaclasses. If no such metaclass exists among the candidates, +the class cannot be created, as explained in :ref:`metaclass-determination`. Finally, the memory layouts of the bases must be compatible. This means that it must be possible to compute a *solid base* for the class. A class is a solid base if it has a @@ -1561,7 +1563,8 @@ depending on the Python implementation. .. impl-detail:: In CPython, many but not all classes defined in C are solid bases, including most - builtins but excluding most concrete :class:`Exception` classes. Generally, a C class + builtins (such as :class:`int` or :class:`BaseException`) + but excluding most concrete :class:`Exception` classes. Generally, a C class is a solid base if its underlying struct is different in size from its base class. Every class has a solid base. :class:`object`, the base class, has itself as its solid base. @@ -1595,7 +1598,7 @@ Example: >>> class C3(SolidChild, Solid1): # solid base is `SolidChild` ... pass >>> - >>> # Error: solid bases are `Solid1` and `Solid2`, but they are not subclasses of each other. + >>> # Error: solid bases are `Solid1` and `Solid2`, but neither is a subclass of the other. >>> class C4(Solid1, Solid2): # error: no single solid base ... pass Traceback (most recent call last): From 2dd55e0dbdf82906fc84c2ff22ace219c9e286a1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 23 Jul 2025 07:50:34 -0700 Subject: [PATCH 5/7] __slots__ to implementation details --- Doc/reference/compound_stmts.rst | 9 +++++---- Doc/reference/datamodel.rst | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index ae6f8ec79cf1e6..8455858b584d08 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1556,13 +1556,14 @@ all other candidate metaclasses. If no such metaclass exists among the candidate the class cannot be created, as explained in :ref:`metaclass-determination`. Finally, the memory layouts of the bases must be compatible. This means that it must be -possible to compute a *solid base* for the class. A class is a solid base if it has a -nonempty :attr:`~object.__slots__` definition; some other classes may also be solid bases, -depending on the Python implementation. +possible to compute a *solid base* for the class. Exactly which classes are solid bases +depends on the Python implementation. .. impl-detail:: - In CPython, many but not all classes defined in C are solid bases, including most + In CPython, a class is a solid base if it has a + nonempty :attr:`~object.__slots__` definition. + Many but not all classes defined in C are also solid bases, including most builtins (such as :class:`int` or :class:`BaseException`) but excluding most concrete :class:`Exception` classes. Generally, a C class is a solid base if its underlying struct is different in size from its base class. diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index 215cde6b6834ec..7af3457070b84a 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -2629,7 +2629,7 @@ Notes on using *__slots__*: * :attr:`~object.__class__` assignment works only if both classes have the same *__slots__*. -* :ref:`Multiple inheritance ` with multiple slotted parent +* :ref:`Multiple inheritance ` with multiple slotted parent classes can be used, but only one parent is allowed to have attributes created by slots (the other bases must have empty slot layouts) - violations raise From 00c39426130c91613d69711e34aefcfa51986b86 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 23 Jul 2025 14:27:14 -0700 Subject: [PATCH 6/7] tweaks --- Doc/reference/compound_stmts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 8455858b584d08..8f52a2a50cdc57 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1516,7 +1516,7 @@ searched when looking up an attribute on a class. See :ref:`python_2.3_mro` for description of how Python determines the MRO for a class. Multiple inheritance is not always allowed. Attempting to define a class with multiple -inheritance will raise an error if one of the bases is invalid, if a consistent MRO +inheritance will raise an error if one of the bases does not allow subclassing, if a consistent MRO cannot be created, if no valid metaclass can be determined, or if there is an instance layout conflict. We'll discuss each of these in turn. @@ -1555,7 +1555,7 @@ child class. It must be a metaclass that is a subclass of all other candidate metaclasses. If no such metaclass exists among the candidates, the class cannot be created, as explained in :ref:`metaclass-determination`. -Finally, the memory layouts of the bases must be compatible. This means that it must be +Finally, the instance layouts of the bases must be compatible. This means that it must be possible to compute a *solid base* for the class. Exactly which classes are solid bases depends on the Python implementation. From 7128c118eab29e5e2c0c6a022d86c420fa9e296c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 24 Jul 2025 07:23:17 -0700 Subject: [PATCH 7/7] Update Doc/reference/compound_stmts.rst Co-authored-by: Akuli --- Doc/reference/compound_stmts.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 8f52a2a50cdc57..e7e84a6a6d788c 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1545,9 +1545,9 @@ resolve a consistent MRO that satisfies these rules from the list of bases provi ... TypeError: Cannot create a consistent method resolution order (MRO) for bases Base, Child -In the MRO of ``Grandchild``, ``Child`` must appear before ``Base`` because it is first -in the base class list, but it must also appear after ``Base`` because it is a child of -``Base``. This is a contradiction, so the class cannot be defined. +In the MRO of ``Grandchild``, ``Base`` must appear before ``Child`` because it is first +in the base class list, but it must also appear after ``Child`` because it is a parent of +``Child``. This is a contradiction, so the class cannot be defined. If some of the bases have a custom :term:`metaclass`, the metaclass of the resulting class is chosen among the metaclasses of the bases and the explicitly specified metaclass of the pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy