web-dev-qa-db-fra.com

Typographies pour Serial Iterable dans Python

J'ai une fonction qui utilise la fonction len sur l'un de ses paramètres et itère sur le paramètre. Maintenant, je peux choisir d'annoter le type avec Iterable ou avec Sized, mais les deux donnent des erreurs dans mypy.

from typing import Sized, Iterable


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

Donne

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

Tandis que

def foo(some_thing: Sized):
...

Donne

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

Puisqu'il n'y a pas de Intersection comme discuté dans ce problème j'ai besoin d'avoir une sorte de classe mixte.

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'])

Cela donne une erreur lors de l'utilisation de foo avec un list.

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

Ce n'est pas trop surprenant puisque:

>>> SizedIterable.__subclasscheck__(list)
False

J'ai donc défini un __subclasshook__ (Voir docs ).

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

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

Ensuite, la vérification de la sous-classe fonctionne:

>>> SizedIterable.__subclasscheck__(list)
True

Mais mypy se plaint toujours de mon list.

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

Comment puis-je utiliser des indications de type lorsque j'utilise à la fois la fonction len et itérer sur mon paramètre? Je pense que la coulée foo(cast(SizedIterable, ['a', 'b', 'c'])) n'est pas une bonne solution.

21
Benjamin

À partir de Python3.6, il existe un nouveau type appelé Collection. Voir ici .

8
Avision

Dans le futur, Protocols sera introduit. Ils sont déjà disponibles via typing_extensions . Voir aussi PEP 544 . En utilisant Protocol le code ci-dessus serait:

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 prend ce code sans se plaindre. Mais PyCharm dit

Type attendu 'SizedIterable', obtenu 'List [str]'

à propos de la dernière ligne.

6
Benjamin

Vous devriez utiliser Sequence from typing si vous prévoyez d'utiliser uniquement list ou Tuple et accéder à ses éléments par index, comme x[0].

1
hans