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.
À partir de Python3.6, il existe un nouveau type appelé Collection
. Voir ici .
Dans le futur, Protocol
s 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.
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]
.