Consejos de escritura para tamaño iterable en Python

3 minutos de lectura

Avatar de usuario de Benjamin
Benjamín

Tengo una función que usa el len funciona en uno de sus parámetros e itera sobre el parámetro. Ahora puedo elegir si anotar el tipo con Iterable o con Sizedpero ambos dan errores en mypy.

from typing import Sized, Iterable


def foo(some_thing: Iterable):
    print(len(some_thing))
    for part in some_thing:
        print(part)

Da

error: Argument 1 to "len" has incompatible type "Iterable[Any]"; expected "Sized"

Tiempo

def foo(some_thing: Sized):
...

Da

error: Iterable expected
error: "Sized" has no attribute "__iter__"

Como no hay Intersection como se discutió en este problema Necesito tener algún tipo de clase mixta.

from abc import ABCMeta
from typing import Sized, Iterable


class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):
    pass


def foo(some_thing: SizedIterable):
    print(len(some_thing))
    for part in some_thing:
        print(part)


foo(['a', 'b', 'c'])

Esto da un error al usar foo con un list.

error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"

Esto no es demasiado sorprendente ya que:

>>> SizedIterable.__subclasscheck__(list)
False

Así que definí un __subclasshook__ (ver documentos).

class SizedIterable(Sized, Iterable[str], metaclass=ABCMeta):

    @classmethod
    def __subclasshook__(cls, subclass):
        return Sized.__subclasscheck__(subclass) and Iterable.__subclasscheck__(subclass)

Entonces la comprobación de la subclase funciona:

>>> SizedIterable.__subclasscheck__(list)
True

Pero mypy todavía se queja de mi list.

error: Argument 1 to "foo" has incompatible type "List[str]"; expected "SizedIterable"

¿Cómo puedo usar sugerencias de tipo cuando uso tanto el len función e iterar sobre mi parámetro? creo que el casting foo(cast(SizedIterable, ['a', 'b', 'c'])) no es una buena solución.

A partir de Python3.6 hay un nuevo tipo llamado Collection. Ver aquí.

  • Es Collection preferible a Iterator cuando el len() se usa la funcion?

    – kctong529

    23 oct 2018 a las 14:34

  • Un Iterator no tiene talla Mira aquí: docs.python.org/3/library/…

    – Una vision

    25/10/2018 a las 11:50


  • PyCharm parece advertir aún cuando es un ItemView (dict.items())

    – usuario972014

    25 de agosto de 2019 a las 7:02

  • Value of type "Collection[Any]" is not indexable… ¿hay un tipo para algo sobre lo que pueda iterar, pueda llamar a len() y obtener un elemento por índice?

    – James Owers

    9 de abril de 2020 a las 16:56

  • @JamesOwers: Esa es una secuencia.

    – usuario2357112

    11 de abril de 2020 a las 11:13

avatar de usuario de hans
hans

deberías ir con Sequence de mecanografía si planea usar solo una lista o una tupla y acceder a sus elementos por índice, como x[0]. Sequence es ambos Sized y Iterablever aquí.

  • Esto no funcionará para conjuntos regulares y congelados. no implementan __getitem__ como se esperaba de las secuencias.

    –Olivier Desenfans

    28 de marzo de 2020 a las 11:04

  • Buen punto. Solo funciona con un indexable (que implementa __getitem__), como una lista o una tupla. En un caso más general, utilice Collectioncomo sugiere Avision.

    – hans

    16 de abril de 2020 a las 9:10


  • Las secuencias son Iterable como consecuencia de necesitar la __getitem__ método, no simplemente ser Iterable.

    – Alex Loss

    11 de agosto de 2020 a las 20:09

En el futuro ProtocolSe introducirán s. Ya están disponibles a través de typing_extensions. Ver también PEP 544. Usando Protocol el codigo de arriba seria:

from typing_extensions import Protocol


class SizedIterable(Protocol):

    def __len__(self):
        pass

    def __iter__(self):
        pass


def foo(some_thing: SizedIterable):
    print(len(some_thing))
    for part in some_thing:
        print(part)


foo(['a', 'b', 'c'])

mypy toma ese código sin quejarse. Pero PyCharm está diciendo

Tipo esperado ‘SizedIterable’, obtuvo ‘Lista[str]’

sobre la última línea.

  • Las definiciones de protocolo ya han sido introducido en Pitón 3.8.

    – Jeyekomon

    16 de febrero de 2021 a las 13:56

Avatar de usuario de Sinri Edogawa
Sinri Edogawa

¿Tal vez necesitas una clase como esta?

class MyArray:
  _array: list

  def as_tuple(self):
    return tuple(self._array)

  def as_list(self):
    return self._array
    
    # More Feature to implement

¿Ha sido útil esta solución?