Ich habe Code, der in Python 3.6 funktioniert hat und in Python 3.8 fehlschlägt. Es scheint sich darauf zu beschränken, die super
Unterklasse wie folgt aufzurufen typing.NamedTuple
:
<ipython-input-2-fea20b0178f3> in <module>
----> 1 class Test(typing.NamedTuple):
2 a: int
3 b: float
4 def __repr__(self):
5 return super(object, self).__repr__()
RuntimeError: __class__ not set defining 'Test' as <class '__main__.Test'>. Was __classcell__ propagated to type.__new__?
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: #def __repr__(self):
...: # return super(object, self).__repr__()
...:
>>> # works
Der Zweck dieses super(object, self).__repr__
Aufrufs besteht darin, den Standard zu verwenden, '<__main__.Test object at 0x7fa109953cf8>'
__repr__
anstatt den gesamten Inhalt der Tupelelemente auszudrucken (was standardmäßig der Fall wäre). Es gibt einige Fragen zusuper
, was zu ähnlichen Fehlern , aber sie:
- Siehe die parameterlose Version
super()
- Fehler bereits in Python 3.6 (es hat bei mir vor 3.6 funktioniert -> 3.8 Upgrade)
- Ich verstehe sowieso nicht, wie ich das beheben kann, da es sich nicht um eine benutzerdefinierte Metaklasse handelt, über die ich die Kontrolle habe, sondern um die von stdlib bereitgestellte
typing.NamedTuple
.
Meine Frage ist, wie ich dies beheben kann, während die Abwärtskompatibilität mit Python 3.6 erhalten bleibt (ansonsten würde ich nur verwenden @dataclasses.dataclass
anstatt von zu erben typing.NamedTuple
).
Eine Nebenfrage ist, wie dies zum Zeitpunkt der Definition fehlschlagen kann , da sich der fehlerhafte super
Aufruf in einer Methode befindet, die noch nicht einmal ausgeführt wurde. Zum Beispiel:
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: def __repr__(self):
...: return foo
funktioniert (bis wir das tatsächlich aufrufen __repr__
), obwohl foo
es sich um eine undefinierte Referenz handelt. Ist super
diesbezüglich magisch?
quelle
super
Objekts selbst, keine Darstellung IhrerTest
Instanz.__repr__ = object.__repr__
in Ihrer Klassendefinition funktioniert für mich unter Python3.6 und Python3.8typing.NamedTuple
.typing.NamedTupleMeta
, das macht ein paar Spielereien.super()
muss__class__
zur Kompilierungszeit verfügbar sein , was hier anscheinend nicht der Fall ist. Siehe auch: Geben Sie ein__classcell__
Beispiel für die Python 3.6-MetaklasseAntworten:
Ich habe mich in der anderen Frage (die ich gerade aktualisiert habe) etwas geirrt . Anscheinend manifestiert sich dieses Verhalten in beiden Fällen von
super
. Im Nachhinein hätte ich das testen sollen.Was hier passiert, ist, dass die Metaklasse
NamedTupleMeta
tatsächlich nicht übergeht__classcell__
,type.__new__
weil sie im laufenden Betrieb ein benanntes Tupel erstellt und dieses zurückgibt. In Pythons 3.6 und 3.7 (wo dies immer noch a istDeprecationWarning
) tritt das__classcell__
Leck in das Klassenwörterbuch ein, da es nicht von entfernt wirdNamedTupleMeta.__new__
.Die
object.__repr__
direkte Verwendung, wie von Azat vorgeschlagen, reicht aus.Auf die gleiche Weise schlägt auch Folgendes fehl:
Während der Erstellung der Klasse werden viele Überprüfungen durchgeführt. Unter diesen ist die Überprüfung , ob die Metaklasse die bestanden hat
__classcell__
übertype_new
.quelle
__classcell__
, hätte ich mir gedacht. Unterstützungsuper
, weiß nicht, genauso wie Mehrfachvererbung kürzlich geändert wurde, um jetzt einen Fehler auszulösen. Es könnte sich dennoch lohnen, ein Problem zu erstellen.Leider bin ich mit CPython-Interna und Klassengenerierung nicht so vertraut, um zu sagen, warum dies fehlschlägt, aber es gibt dieses CPython-Bug-Tracker-Problem, das verwandt zu sein scheint, und einige Wörter in Python-Dokumenten
Wahrscheinlich
namedtuple
haben wir irgendwo während der eigentlichen Erstellung einen Anruftype.__new__
ohne__classcell__
Propagierung, aber ich weiß nicht, ob dies der Fall ist.Aber dieser spezielle Fall scheint lösbar zu sein, wenn man den
super()
Aufruf nicht verwendet und explizit sagt, dass "wir eine__repr__
Methode derobject
Klasse haben müssen" wiequelle