es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

La herencia de Generic y NamedTuple falla con el error “falta el argumento posicional requerido”.

Me he encontrado con un problema al intentar definir un NamedTuple genérico (typing/mypy Generic). He logrado reducirlo al ejemplo de trabajo más pequeño posible:

a.py:

from typing import NamedTuple
from typing import Generic
from typing import TypeVar

T = TypeVar('T')

class A(Generic[T], NamedTuple('A', [('x', T)])):
    pass

a = A(12)

Esto funciona perfectamente en todas las versiones de Python que tenemos que admitir (incluyendo 3.4, 3.7), excepto en la versión 3.5.3, donde falla:

$ python3 a.py
Traceback (most recent call last):
  File "a.py", line 10, in <module>
    a = A(12)
  File "/usr/lib/python3.5/typing.py", line 1126, in __new__
    return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
  File "/usr/lib/python3.5/typing.py", line 1087, in _generic_new
    return base_cls.__new__(cls)
TypeError: __new__() missing 1 required positional argument: 'x'

He intentado agregar manualmente el método __new__:

class A(Generic[T], NamedTuple('A', [('x', T)])):
    def __new__(cls, a):
        return super(A, cls).__new__(cls, a)

pero falla con exactamente el mismo mensaje de error.

Nombrar explícitamente el parámetro x (a = A(x=12)) también arroja el mismo mensaje de error.

¿Hay alguna manera de hacer que funcione?

Por favor, no me digas que actualice Python, ya que admitimos varias versiones de Python y 3.5.3 es una de ellas. Actualizar Python no es una opción aquí.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Parece que la causa del error es la implementación de la clase NamedTuple en Python 3.5.3. Una solución temporal para este problema podría ser definir el método __init__ en la clase A y llamar al método super().__init__() con el argumento x pasado como una tupla:

    class A(Generic[T], NamedTuple('A', [('x', T)])):
        def __init__(self, x: T) -> None:
            super().__init__(x)
    
    a = A(12)
    

    Esto debería funcionar tanto en Python 3.5.3 como en otras versiones. Sin embargo, ten en cuenta que al definir manualmente __init__, no obtendremos la implementación por defecto de __new__ que proporciona NamedTuple. Por lo tanto, si queremos realizar personalizaciones adicionales en el proceso de instanciación, también deberemos definir nuestra propia implementación de __new__.

Comments are closed.