web-dev-qa-db-fra.com

Python type hinting: comment savoir que X est une sous-classe pour Foo?

Comment dois-je écrire un indice de type pour les types de classe en Python? Considérez ce code:

class A(object):
    pass

class B(A):
    pass

def register(cls: type[A]):
    assert issubclass(cls, A)

 register(A)
 register(B)

Est type[A] la bonne façon d'écrire ceci? Si je devais simplement utiliser cls: A cela voudrait dire cls est une instance de A, mais je veux dire cls est une classe/type, qui au moins sous-classe A .

Plus précisément, ce que je veux indiquer, c'est que le paramètre doit être un type de modèle Django.

32
vdboor

Il semble que les autres réponses actuelles (22 septembre 2016) soient incorrectes. Selon le PEP 484 (sur les conseils de type), il existe un conseil pour le type d'objets de classe, appelé Type [C] . Et selon la documentation du module typing, vous pouvez utiliser typing.Type [C] pour obtenir exactement ce que vous voulez. Je les utilise moi-même avec Python 3.5.2.

Citant le PEP :

Parfois, vous voulez parler des objets de classe, en particulier des objets de classe qui héritent d'une classe donnée. Cela peut être orthographié comme Type [C] où C est une classe. Pour clarifier: alors que C (lorsqu'il est utilisé comme annotation) fait référence à des instances de classe C, le type [C] fait référence à des sous-classes de C.

Et en citant les docs :

Une variable annotée avec C peut accepter une valeur de type C. En revanche, une variable annotée avec Type [C] peut accepter des valeurs qui sont des classes elles-mêmes - en particulier, elle acceptera l'objet classe de C.

Et en vous référant à votre exemple spécifique:

import typing

class A(object):
    pass

class B(A):
    pass

def register(cls: typing.Type[A]):
    assert issubclass(cls, A)

register(A)
register(B)

Vous pouvez vérifier ce code statiquement en utilisant mypy , et cela devrait fonctionner dans des cas simples - attention cependant que mypy est un travail en cours, à partir de maintenant il y a plusieurs problèmes ouverts concernant l'indication de type [C].

32
mbdevpl

Pour résoudre votre cas général, vous devez écrire une métaclasse avec un __subclasscheck__ Approprié. Possible, mais encombrant.

Dans votre cas spécifique de classes de modèles Django, une métaclasse explicite existe déjà, donc annoter qui devrait faire le travail:

import Django.db.model as model

def register(cls: model.base.ModelBase): ...

Cela fonctionnera car isinstance(models.Model, models.base.ModelBase) est vrai.

3
Lutz Prechelt