Tengo un código que funcionó en Python 3.6 y falla en Python 3.8. Parece reducirse a llamar super
en la subclase de typing.NamedTuple
, como se muestra a continuación:
<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
El propósito de esta super(object, self).__repr__
llamada es usar el estándar en '<__main__.Test object at 0x7fa109953cf8>'
__repr__
lugar de imprimir todo el contenido de los elementos de tupla (lo que sucedería de manera predeterminada). Hay algunas preguntas sobre el super
resultado de errores similares pero:
- Consulte la versión sin parámetros
super()
- Fallo ya en Python 3.6 (funcionó para mí antes de la actualización 3.6 -> 3.8)
- No entiendo cómo solucionar esto de todos modos, dado que no es una metaclase personalizada sobre la que tengo control sino el stdlib proporcionado
typing.NamedTuple
.
Mi pregunta es ¿cómo puedo solucionar esto mientras mantengo la compatibilidad con Python 3.6 (de lo contrario, solo usaría en @dataclasses.dataclass
lugar de heredar de typing.NamedTuple
)?
Una pregunta secundaria es cómo puede fallar esto en el momento de la definición dado que la super
llamada infractora está dentro de un método que aún no se ha ejecutado. Por ejemplo:
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: def __repr__(self):
...: return foo
funciona (hasta que realmente llamemos al __repr__
) aunque foo
sea una referencia indefinida. ¿Es super
mágico en ese sentido?
fuente
super
objeto en sí, no una representación de suTest
instancia.__repr__ = object.__repr__
en su definición de clase me funciona en Python3.6 y Python3.8typing.NamedTuple
;typing.NamedTupleMeta
, eso está haciendo algunas travesuras.super()
requiere__class__
estar disponible en tiempo de compilación , que no es el caso aquí, aparentemente. Vea también: Proporcione un__classcell__
ejemplo para la metaclase Python 3.6Respuestas:
Estaba un poco equivocado en la otra pregunta (que acabo de actualizar). Aparentemente, este comportamiento se manifiesta en ambos casos de
super
. En retrospectiva, debería haber probado esto.¿Qué está pasando aquí es la metaclase
NamedTupleMeta
de hecho no pasa__classcell__
atype.__new__
porque crea un namedtuple sobre la marcha y rendimientos. En realidad, en Python 3.6 y 3.7 (donde esto todavía es aDeprecationWarning
), se__classcell__
filtra en el diccionario de la clase ya que no se eliminaNamedTupleMeta.__new__
.Usar
object.__repr__
directamente como lo sugiere Azat hace el truco.De la misma manera, lo siguiente también falla:
Se realizan muchas verificaciones mientras se construye la clase. Entre estos, está verificando si la metaclase ha pasado
__classcell__
atype_new
.fuente
__classcell__
, habría pensado así. Apoyosuper
, no sé, de la misma manera que la herencia múltiple cambiada recientemente para generar un error. Sin embargo, podría valer la pena crear un problema.Desafortunadamente, no estoy tan familiarizado con la generación interna de clases y CPython para decir por qué falla, pero existe este problema del rastreador de errores de CPython que parece estar relacionado y algunas palabras en documentos de Python
así que probablemente en algún lugar durante la
namedtuple
creación real tengamos una llamadatype.__new__
sin__classcell__
propagación, pero no sé si es el caso.Pero este caso particular parece resolverse al no utilizar la
super()
llamada con decir explícitamente que "necesitamos tener un__repr__
método de laobject
clase" comofuente