Especifique la longitud de la secuencia o la lista con el módulo de escritura de Python

6 minutos de lectura

Avatar de usuario de John Brodie
Juan Brodie

Estoy dando el Python typing módulo un tiro.

Sé que es válido especificar la longitud de un List como el siguiente*:

List[float, float, float]   # List of 3 floats <-- NOTE: this is not valid Python

¿Hay alguna abreviatura para listas más largas? ¿Qué pasa si quiero configurarlo en 10 flotadores?

List[float * 10]   # This doesn't work.

Cualquier idea si esto es posible, esto sería útil.


*NOTA: Resulta que proporcionar múltiples argumentos a Sequence[] (y sus subclases) de esta manera actualmente NO es Python válido. Además, actualmente no es posible especificar un Sequence longitud usando el typing módulo de esta manera.

no puedes Una lista es mutable, Longitud variable estructura. Si necesita una estructura de longitud fija, use una tupla en su lugar:

Tuple[float, float, float, float, float, float, float, float, float, float]

O mejor aún, usa un tupla nombradaque tiene tanto índices como atributos con nombre:

class BunchOfFloats(NamedTuple):
    foo: float
    bar: float
    baz: float
    spam: float
    ham: float
    eggs: float
    monty: float
    python: float
    idle: float
    cleese: float

Una lista es simplemente el tipo de datos incorrecto para una estructura de datos de longitud fija.

  • Si está usando tupla, también puede usar puntos suspensivos literales, es decir Tuple[int, ...] según PEP484

    –Tomász Bartkowiak

    23 de julio de 2019 a las 10:03


  • @TomaszBartkowiak: ese es el opuesto de lo que se pregunta. Sí, puede declarar una tupla de longitud variable que contenga un solo tipo de esa manera. Pero eso es no es un tamaño fijo.

    – Martijn Pieters

    23 de julio de 2019 a las 11:16


  • A veces, desea un contenedor mutable que tenga una longitud fija. Por ejemplo, si desea iniciar los elementos del contenedor en Ninguno, pero luego actualice los elementos con nuevos valores. Pero el contenedor seguiría siendo de tamaño fijo.

    – Mate

    23 de julio de 2020 a las 0:55

  • @Matt: claro, pero no hay un tipo de Python incorporado que le permita hacer eso, por lo que tampoco hay sugerencias de tipo.

    – Martijn Pieters

    25 de julio de 2020 a las 15:52

  • Para su información, la primera sugerencia de @MartijnPieters se puede abreviar como Tuple[10 * (float, )]que me parece bastante breve y ordenado, ya que expresa su propósito con mucha claridad.

    – balú

    27 de abril de 2021 a las 8:34

Avatar de usuario de Zaffy
Zaffy

typing.Annotated puede ser útil aquí. Le permite especificar metadatos arbitrarios para escribir sugerencias:

Annotated[list[float], 3]

Nuevo en Annotated? Aquí hay un fragmento de los documentos:

Si una biblioteca (o herramienta) encuentra una sugerencia de tipo Annotated[T, x] y no tiene una lógica especial para los metadatos xdebe ignorarlo y simplemente tratar el tipo como T.

Notablemente, mypy tiene un solicitud pendiente (abierto a partir de noviembre de 2022) para algo como esto. Mientras tanto, piensa en Annotated en cuanto a la legibilidad del desarrollador, no para la inspección automatizada (a menos que desarrolle herramientas de inspección).

  • Gracias, es bueno saber eso typing.Annotated existe — En mi humilde opinión, la respuesta debería explicar que el entero agregado 3 funciona como un comentario adjunto a la variable anotada. Tiene que escribir sus propias herramientas para usar la anotación adicional. — Además de eso, agregar solo un número entero hará que la anotación adicional sea ambigua. Sería mejor crear una estructura para poder expresar el contexto, algo como: Annotated[List[float], Length[3]]

    – pabouk – Ucrania se mantiene fuerte

    16 de mayo a las 15:01

  • no estoy viendo typing.Length – ¿Eso realmente existe, o simplemente expresas el deseo de una nueva función?

    -Addison Klinke

    27 de julio a las 15:25

Avatar de usuario de Raymond Hettinger
raymond hettinger

Hasta ahora, solo las tuplas admiten especificar un número fijo de campos y no tiene atajos para un número fijo de repeticiones.

Aquí está la definición y la cadena de documentación del mecanografía módulo:

class Tuple(tuple, extra=tuple, metaclass=TupleMeta):
    """Tuple type; Tuple[X, Y] is the cross-product type of X and Y.

    Example: Tuple[T1, T2] is a tuple of two elements corresponding
    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
    of an int, a float and a string.

    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
    """

    __slots__ = ()

    def __new__(cls, *args, **kwds):
        if _geqv(cls, Tuple):
            raise TypeError("Type Tuple cannot be instantiated; "
                            "use tuple() instead")
        return _generic_new(tuple, cls, *args, **kwds)

Dado que las listas son un tipo mutable de longitud variable, no tiene ningún sentido utilizar una declaración de tipo para especificar un tamaño fijo.

  • Gracias Raymond, lo suficientemente claro. Si bien las dos respuestas que he recibido aquí son precisas y aclaratorias, todavía no estoy 100% seguro de cuál es la mejor manera de sugerir funciones que realmente necesitan una entrada de secuencia de longitud establecida. Supongo que simplemente poner esto en la cadena de documentación no es tan malo, pero parece una pena. (Realmente disfruto cómo PyCharm capta estas sugerencias en la ayuda generada para cada método)

    – John Brodie

    7 ago 2017 a las 15:39


  • “hasta ahora…” ¿Existe algún plan para especificar una secuencia mutable de longitud fija? Generic en el typing módulo en algún momento?

    – Rick

    5 de febrero de 2018 a las 16:35

Cuando también me enfrenté al mismo problema, no me agradó ver la respuesta de Martijn Pieters. Ya que quería una forma “rápida” y “fácil” de resolver este problema.

Así que probé las otras sugerencias enumeradas aquí primero.

Nota: utilicé VSCode con Pylance como servidor de idioma

La respuesta de Zaffy fue mi favorita.

def demystify(mystery: Annotated[Tuple[int], 6]):
    a, b, c, d, e, f = mystery
    print(a, b, c, d, e, f)

Sugerencia para la función entonces se ve así: demystify: (mystery: Tuple[int]) -> None
También recibo un error de Pylance Tuple size mismatch: expected 6 but received para la línea a, b, c, d, e, f = mystery

Luego probé Tuple[6 * (int, )] que fue mencionado por balu en los comentarios de la respuesta de Martijn Pieters

def demystify(mystery: Tuple[6 * (int,)]):
    a, b, c, e, f, g = mystery
    print(a, b, c, e, f, g)

Resultando en el mismo error de Pylance que antes. La pista para la función fue esta: demystify: (mystery: Tuple[Tuple[Type[int], ...]]) -> None

Volviendo a escribir la longitud esperada:

def demystify(mystery: Tuple[int, int, int, int, int, int]):
    a, b, c, e, f, g = mystery
    print(a, b, c, e, f, g)

Esto resolvió el error de Pylance y me dio una sugerencia de función “clara”: demystify: (mystery: Tuple[int, int, int, int, int, int]) -> None

Pero al igual que John Brodie, no estaba contento con esta solución.

Ahora volvamos a la respuesta, al principio, no deseada:

class MysteryType(NamedTuple):
    a: int
    b: int
    c: int
    d: int
    e: int
    f: int
    g: int

def demystify(mystery: MysteryType):
    print(*mystery)

La sugerencia de función ahora parece más mística: demystify: (mystery: MysteryType) -> None pero crear un nuevo MysteryType me da toda la información que necesito: (a: int, b: int, c: int, d: int, e: int, f: int, g: int)

También puedo usar MysteryType en otros métodos y funciones sin la necesidad de contar las sugerencias de tipo.

Entonces, para resumir una larga historia y parafrasear el Zen de Python:

NamedTuples es una gran idea, ¡hagamos más de eso!

¿Ha sido útil esta solución?