Welche Klassen können nicht in Unterklassen eingeteilt werden?

75

Gibt es eine Regel darüber, welche integrierten und Standardbibliotheksklassen nicht in Unterklassen unterteilt werden können ("final")?

Ab Python 3.3 sind hier einige Beispiele:

  • bool
  • function
  • operator.itemgetter
  • slice

Ich habe eine Frage gefunden , die sich mit der Implementierung von "finalen" Klassen sowohl in C als auch in reinem Python befasst.

Ich würde gerne verstehen, welche Gründe erklären können, warum eine Klasse überhaupt als "endgültig" ausgewählt wird.

max
quelle
5
NoneTypeist ein weiteres Beispiel.
Duncan
4
Kann eine letzte Klasse in einer Python-Implementierung in einer anderen Implementierung unterklassifiziert werden? Ich hoffe, jemand kann bestätigen, dass es nie passiert. Andernfalls kann Code, der für eine Implementierung geschrieben wurde, beschädigt werden, wenn er auf eine andere portiert wird (auch sehr schmerzhaft: Stellen Sie sich vor, jemand hat eine Unterklasse functionund muss den Code jetzt umgestalten, um diese Vererbung zu vermeiden).
Max
3
Beachten Sie, dass PyPy sich auch weigert, Ihre vier Beispiele zu unterordnen ... obwohl es keine CPython-Einschränkung gibt. Möglicherweise ist in ihrer Codebasis ein Grund dokumentiert.
Matt B.
4
NotImplementedType(dh type(NotImplemented)) und ellipsis(dh type(...)) sind zwei weitere Beispiele. Wie None, gibt es keinen Grund mehr als eine Instanz dieser Klassen zu haben, und wenn dies erlaubt wäre, würde es schwierig sein , für sie zu überprüfen ( if x is Nonewürde müssen if isinstance(x, type(None))). Ich nehme an, Sie könnten im Prinzip eine vollständige Liste erhalten, indem Sie in der Quelle nach dem Wert von tp_flagsin Typdefinitionen suchen .
James
2
@agf: In diesem Beitrag erfahren Sie, warum Sie eine Unterklasse erstellen möchten itemgetter. und dieser Beitrag, warum Sie möglicherweise eine Unterklasse erstellen möchten function(dieser ist alt, daher gelten einige dieser Argumente möglicherweise nicht).
Max

Antworten:

31

Es scheint zwei Gründe dafür zu geben, dass eine Klasse in Python "endgültig" ist.

1. Verletzung der Klasseninvariante

Klassen, die dem Singleton-Muster folgen, haben die Invariante, dass es eine begrenzte (vorher festgelegte) Anzahl von Instanzen gibt. Jeder Verstoß gegen diese Invariante in einer Unterklasse widerspricht der Absicht der Klasse und würde nicht richtig funktionieren. Beispiele:

  • bool: True, False; siehe Guidos Kommentare
  • NoneType:: None
  • NotImplementedType:: NotImplemented
  • ellipsis:: Ellipsis

Es kann andere Fälle als das Singleton-Muster in dieser Kategorie geben, aber mir sind keine bekannt.

2. Kein überzeugender Anwendungsfall

Eine in C implementierte Klasse erfordert zusätzliche Arbeit, um Unterklassen zu ermöglichen (zumindest in CPython). Solche Arbeiten ohne einen überzeugenden Anwendungsfall durchzuführen, ist nicht sehr attraktiv, daher ist es weniger wahrscheinlich, dass Freiwillige sich melden. Beispiele:

Anmerkung 1:

Ich dachte ursprünglich, es gäbe gültige Anwendungsfälle, aber einfach unzureichendes Interesse an der Unterklasse von functionund operator.itemgetter. Vielen Dank an @agf für den Hinweis, dass die hier und hier angebotenen Anwendungsfälle nicht überzeugend sind (siehe @agf-Kommentare zur Frage).

Anmerkung 2:

Ich befürchte, dass eine andere Python-Implementierung versehentlich die Unterklasse einer Klasse zulässt, die in CPython endgültig ist. Dies kann zu nicht portierbarem Code führen (ein Anwendungsfall kann schwach sein, aber jemand schreibt möglicherweise immer noch Code, der in Unterklassen unterteilt ist, functionwenn sein Python dies unterstützt). Dies kann behoben werden, indem in der Python-Dokumentation alle integrierten und Standardbibliotheksklassen markiert werden, die nicht in Unterklassen unterteilt werden können, und dass alle Implementierungen in dieser Hinsicht dem CPython-Verhalten folgen müssen.

Notiz 3:

Die von CPython in allen oben genannten Fällen erzeugte Nachricht lautet:

TypeError: type 'bool' is not an acceptable base type

Es ist ziemlich kryptisch, wie zahlreiche Fragen zu diesem Thema zeigen. Ich werde einen Vorschlag einreichen, um der Dokumentation einen Absatz hinzuzufügen, in dem die letzten Klassen erläutert werden, und möglicherweise sogar die Fehlermeldung in Folgendes ändern:

TypeError: type 'bool' is final (non-extensible)
max
quelle
5
Natürlich, wenn Sie eine Simulation von Schrödingers Katze geschrieben haben, möchten Sie vielleicht folgende Unterklasse booleinschließen unknown:-P nur ein Scherz
Endophage
9
@Endophage: Ein Qbit wäre sicherlich ein nützlicher Typ, aber es ist keine Unterklasse bool, im Gegenteil, jeder einzelne boolesche Wert ist eine Art Qbit! eine, die zufällig beobachtet wird. Ich würde diesen Fall wahrscheinlich mit __subclasscheck__und Freunden behandeln.
SingleNegationElimination
5
@TokenMacGuy ausgezeichnete Beobachtung! Warum haben wir nicht eine Qbit-Klasse in Python, von der bool eine Unterklasse ist? Ich fordere es sollte so sein! :-P
Endophage
8
und was ist mit slice?
Wap26
Der Wert eines Qbits ist jedoch immer Falsch oder Wahr, wenn er beobachtet / getestet wird. Anscheinend brauchen Sie nur eine Unterklasse von Bool, die ihre "Berechnung" nur bei Beobachtung durchführt .... ( LazyBool)
DylanYoung