Eu tenho um código que funcionou no Python 3.6 e falha no Python 3.8. Parece resumir-se a chamar a super
subclasse de typing.NamedTuple
, como abaixo:
<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
O objetivo desta super(object, self).__repr__
chamada é usar o padrão em '<__main__.Test object at 0x7fa109953cf8>'
__repr__
vez de imprimir todo o conteúdo dos elementos da tupla (o que aconteceria por padrão). Existem algumas perguntas sobre como super
resultar em erros semelhantes, mas eles:
- Consulte a versão sem parâmetros
super()
- Já falhei no Python 3.6 (funcionou para mim antes da atualização 3.6 -> 3.8)
- Não consigo entender como corrigir isso de qualquer maneira, já que não é uma metaclasse personalizada sobre a qual tenho controle, mas a fornecida pelo stdlib
typing.NamedTuple
.
Minha pergunta é como posso corrigir isso, mantendo a compatibilidade com o Python 3.6 (caso contrário, eu usaria em @dataclasses.dataclass
vez de herdar typing.NamedTuple
)?
Uma questão paralela é como isso pode falhar no momento da definição, uma vez que a super
chamada incorreta está dentro de um método que ainda nem foi executado. Por exemplo:
In [3]: class Test(typing.NamedTuple):
...: a: int
...: b: float
...: def __repr__(self):
...: return foo
funciona (até chamarmos de __repr__
) mesmo que foo
seja uma referência indefinida. É super
mágico a esse respeito?
fonte
super
próprio objeto, não uma representação da suaTest
instância.__repr__ = object.__repr__
em sua definição de classe funciona para mim em Python3.6 e Python3.8typing.NamedTuple
;typing.NamedTupleMeta
, isso está fazendo algumas travessuras.super()
requer__class__
estar disponível em tempo de compilação , o que não é o caso aqui, aparentemente. Veja também: Forneça um__classcell__
exemplo para a metaclasse do Python 3.6Respostas:
Eu estava um pouco errado na outra pergunta (que acabei de atualizar). Aparentemente, esse comportamento se manifesta nos dois casos de
super
. Em retrospectiva, eu deveria ter testado isso.O que está acontecendo aqui é que a metaclasse
NamedTupleMeta
não passa__classcell__
detype.__new__
fato para ela, porque cria um duplo nomeado em tempo real e o devolve. Na verdade, nas versões 3.6 e 3.7 do Python (onde ainda é umDeprecationWarning
), o__classcell__
vazamento no dicionário de classes, uma vez que não é removido porNamedTupleMeta.__new__
.Usar
object.__repr__
diretamente como sugerido por Azat faz o truque.Da mesma maneira que o seguinte também falha:
Muitas verificações são realizadas enquanto a classe está sendo construída. Entre eles, está verificando se a metaclasse passou
__classcell__
para otype_new
.fonte
__classcell__
, eu teria pensado assim. Suportesuper
, não sei, da mesma maneira que a herança múltipla recentemente alterada para agora gerar um erro. Ainda pode valer a pena criar um problema.Infelizmente, eu não estou tão familiarizado com o interno do CPython e a geração de classes para dizer por que ele falha, mas há esse problema do rastreador de erros do CPython que parece estar relacionado e algumas palavras nos documentos do Python
então, provavelmente em algum lugar durante a
namedtuple
criação real , temos uma chamadatype.__new__
sem__classcell__
propagação, mas não sei se é o caso.Mas esse caso em particular parece ser solucionável ao não usar
super()
call dizendo explicitamente que "precisamos ter o__repr__
método daobject
classe" comofonte