Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

241
Vistas
escribiendo.NamedTuple y argumentos predeterminados mutables

Dado que quiero usar correctamente las anotaciones de tipo para las tuplas con nombre del módulo de escritura:

 from typing import NamedTuple, List class Foo(NamedTuple): my_list: List[int] = [] foo1 = Foo() foo1.my_list.append(42) foo2 = Foo() print(foo2.my_list) # prints [42]

¿Cuál es la mejor o la forma más limpia de evitar la miseria del valor predeterminado mutable en Python? Tengo algunas ideas, pero nada parece realmente bueno.

  1. Uso de None como predeterminado

     class Foo(NamedTuple): my_list: Optional[List[int]] = None foo1 = Foo() if foo1.my_list is None foo1 = foo1._replace(my_list=[]) # super ugly foo1.my_list.append(42)
  2. Sobrescribir __new__ o __init__ no funcionará:

     AttributeError: Cannot overwrite NamedTuple attribute __init__ AttributeError: Cannot overwrite NamedTuple attribute __new__
  3. Especial @classmethod

     class Foo(NamedTuple): my_list: List[int] = [] @classmethod def use_me_instead(cls, my_list=None): if not my_list: my_list = [] return cls(my_list) foo1 = Foo.use_me_instead() foo1.my_list.append(42) # works!
  4. ¿Quizás usar frozenset y evitar los atributos mutables por completo? Pero eso no funcionará con Dict s ya que no hay frozendict s.

¿Alguien tiene una buena respuesta?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

Utilice una clase de datos en lugar de una tupla con nombre. Una clase de datos permite que un campo especifique una fábrica predeterminada en lugar de un único valor predeterminado.

 from dataclasses import dataclass, field @dataclass(frozen=True) class Foo: my_list: List[int] = field(default_factory=list)
over 4 years ago · Santiago Trujillo Denunciar

0

EDITAR : Actualizado para usar el enfoque de Alex, porque esto funciona mucho mejor que mi idea anterior.

Aquí hay una clase de Foo de Alex puesta en un decorador:

 from typing import NamedTuple, List, Callable, TypeVar, cast, Type T = TypeVar('T') def default_factory(**factory_kw: Callable) -> Callable[[Type[T]], Type[T]]: def wrapper(wcls: Type[T]) -> Type[T]: def du_new(cls: Type[T], **kwargs) -> T: for key, factory in factory_kw.items(): if key not in kwargs: kwargs[key] = factory() return super(cls, cls).__new__(cls, **kwargs) # type: ignore[misc] return type(f'{wcls.__name__}_', (wcls, ), {'__new__': du_new}) return wrapper @default_factory(my_list=list) class Foo(NamedTuple): my_list: List[int] = [] # you still need to define the default argument foo1 = Foo() foo1.my_list.append(42) foo2 = Foo() print(foo2.my_list) # prints [] #reveal_type(foo2) # prints Tuple[builtins.list[builtins.int], fallback=foo.Foo]
over 4 years ago · Santiago Trujillo Denunciar

0

EDITAR :

Combinando mi enfoque con la idea de Sebastian Wagner de usar un decorador , podemos lograr algo como esto:

 from typing import NamedTuple, List, Callable, TypeVar, Type, Any, cast from functools import wraps T = TypeVar('T') def default_factory(**factory_kw: Callable[[], Any]) -> Callable[[Type[T]], Type[T]]: def wrapper(wcls: Type[T], /) -> Type[T]: @wraps(wcls.__new__) def __new__(cls: Type[T], *args: Any, **kwargs: Any) -> T: for key, factory in factory_kw.items(): kwargs.setdefault(key, factory()) new = super(cls, cls).__new__(cls, *args, **kwargs) # type: ignore[misc] # This call to cast() is necessary if you run MyPy with the --strict argument return cast(T, new) cls_name = wcls.__name__ wcls.__name__ = wcls.__qualname__ = f'_{cls_name}' return type(cls_name, (wcls, ), {'__new__': __new__, '__slots__': ()}) return wrapper @default_factory(my_list=list) class Foo(NamedTuple): # You do not *need* to have the default value in the class body, # but it makes MyPy a lot happier my_list: List[int] = [] foo1 = Foo() foo1.my_list.append(42) foo2 = Foo() print(f'foo1 list: {foo1.my_list}') # prints [42] print(f'foo2 list: {foo2.my_list}') # prints [] print(Foo) # prints <class '__main__.Foo'> print(Foo.__mro__) # prints (<class '__main__.Foo'>, <class '__main__._Foo'>, <class 'tuple'>, <class 'object'>) from inspect import signature print(signature(Foo.__new__)) # prints (_cls, my_list: List[int] = [])

Ejecútelo a través de MyPy, y MyPy nos informa que el tipo revelado de foo1 y foo2 sigue siendo "Tuple[builtins.list[builtins.int], fallback=__main__.Foo]"

Respuesta original a continuación.


¿Qué tal esto? (Inspirado por esta respuesta aquí ):

 from typing import NamedTuple, List, Optional, TypeVar, Type class _Foo(NamedTuple): my_list: List[int] T = TypeVar('T', bound="Foo") class Foo(_Foo): "A namedtuple defined as `_Foo(mylist)`, with a default value of `[]`" __slots__ = () def __new__(cls: Type[T], mylist: Optional[List[int]] = None) -> T: mylist = [] if mylist is None else mylist return super().__new__(cls, mylist) # type: ignore f, g = Foo(), Foo() print(isinstance(f, Foo)) # prints "True" print(isinstance(f, _Foo)) # prints "True" print(f.mylist is g.mylist) # prints "False"

Ejecútelo a través de MyPy y el tipo revelado de f y g será: "Tuple[builtins.list[builtins.int], fallback=__main__.Foo]" .

No estoy seguro de por qué tuve que agregar el # type: ignore para que MyPy dejara de quejarse; si alguien me puede aclarar eso, estaría interesado. Parece funcionar bien en tiempo de ejecución.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda