Python 3 et typage statique

Je n'ai pas vraiment porté autant d'attention au développement de Python 3 que je l'aurais souhaité, et j'ai juste remarqué quelques changements de syntaxe intéressants. Spécifiquement de cette réponse SO annotation de paramètre de fonction:

def digits(x:'nonnegative number') -> "yields number's digits":
    # ...

ne sachant rien à ce sujet, j'ai pensé qu'il pourrait peut-être être utilisé pour implémenter le typage statique en Python!

après quelques recherches, il a semblé y avoir beaucoup de discussion concernant (entièrement facultatif) le typage statique en Python, tel que mentionné dans PEP 3107 , et "Ajouter en Option le Typage Statique pour Python" (et partie 2 )

..mais, je ne sais pas jusqu'où cela a progressé. Existe-il des implémentations de typage statique, en utilisant le paramètre-annotation? Est-ce que l'une des idées paramétrées en Python 3?

54
demandé sur Community 2009-08-14 05:47:26

5 réponses

Merci d'avoir lu mon code!

en effet, il n'est pas difficile de créer un exécuteur d'annotation générique en Python. Voici mon point de vue:

'''Very simple enforcer of type annotations.

This toy super-decorator can decorate all functions in a given module that have 
annotations so that the type of input and output is enforced; an AssertionError is
raised on mismatch.

This module also has a test function func() which should fail and logging facility 
log which defaults to print. 

Since this is a test module, I cut corners by only checking *keyword* arguments.

'''

import sys

log = print


def func(x:'int' = 0) -> 'str':
    '''An example function that fails type checking.'''
    return x


# For simplicity, I only do keyword args.
def check_type(*args):
    param, value, assert_type = args
    log('Checking {0} = {1} of {2}.'.format(*args))
    if not isinstance(value, assert_type):
        raise AssertionError(
            'Check failed - parameter {0} = {1} not {2}.'
            .format(*args))
    return value

def decorate_func(func):    
    def newf(*args, **kwargs):
        for k, v in kwargs.items():
            check_type(k, v, ann[k])
        return check_type('<return_value>', func(*args, **kwargs), ann['return'])

    ann = {k: eval(v) for k, v in func.__annotations__.items()}
    newf.__doc__ = func.__doc__
    newf.__type_checked = True
    return newf

def decorate_module(module = '__main__'):
    '''Enforces type from annotation for all functions in module.'''
    d = sys.modules[module].__dict__
    for k, f in d.items():
        if getattr(f, '__annotations__', {}) and not getattr(f, '__type_checked', False):
            log('Decorated {0!r}.'.format(f.__name__))
            d[k] = decorate_func(f)


if __name__ == '__main__':
    decorate_module()

    # This will raise AssertionError.
    func(x = 5)

étant donné cette simplicité, il est étrange à première vue que cette chose n'est pas mainstream. Cependant, je crois qu'il y a de bonnes raisons pour lesquelles c'est pas aussi utile qu'il pourrait sembler . Généralement, la vérification de type aide parce que si vous ajoutez entier et dictionnaire, les chances sont vous faites une erreur évidente (et si vous voulez dire quelque chose de raisonnable, c'est quand même mieux vaut être explicite qu'implicite ).

mais dans la vie réelle vous mélangez souvent des quantités du même type d'ordinateur comme vu par le compilateur mais clairement différent type humain , par exemple l'extrait suivant contient une erreur évidente:

height = 1.75 # Bob's height in meters.
length = len(sys.modules) # Number of modules imported by program.
area = height * length # What's that supposed to mean???

tout humain devrait immédiatement voir une erreur dans le au-dessus de la ligne à condition qu'il connaisse le 'type humain' des variables height et length même si il regarde à l'ordinateur comme parfaitement légal multiplication de int et float .

il y a plus à dire sur les solutions possibles à ce problème, mais faire respecter les "types d'ordinateurs" est apparemment une demi-solution, donc, du moins à mon avis, c'est pire qu'aucune solution du tout . C'est la même raison pourquoi systèmes hongrois est une idée terrible tandis que Apps hongrois est une grande. Il y a plus sur le très instructif poste de Joel Spolsky .

maintenant si quelqu'un devait implémenter une sorte de bibliothèque pythonique tierce partie qui assignerait automatiquement aux données du monde réel son type humain et a ensuite pris soin de transformer ce type comme width * height -> area et d'appliquer que vérifier avec les annotations de fonction, je pense que ce serait un type vérifiant les gens pourraient vraiment utiliser!

33
répondu ilya n. 2009-08-14 08:19:38

comme mentionné dans ce PEP, la vérification de type statique est l'une des applications possibles pour lesquelles les annotations de fonctions peuvent être utilisées, mais ils laissent à des bibliothèques tierces le soin de décider comment le faire. Autrement dit, il n'y aura pas d'implémentation officielle dans le noyau de python.

en ce qui concerne les mises en œuvre par des tiers, il y a quelques bribes (comme http://code.activestate.com/recipes/572161 / ), qui semblent faire le travail assez bien.

EDIT:

comme note, je tiens à mentionner que le comportement de vérification est préférable à la vérification de type, donc je pense que la vérification de type statique n'est pas une si grande idée. Ma réponse ci-dessus vise à répondre à la question, non pas parce que je ferais la vérification de moi-même d'une telle manière.

14
répondu sykora 2009-08-14 07:51:55

"static typing" en Python ne peut être implémenté que pour que la vérification de type soit effectuée en temps d'exécution, ce qui signifie qu'elle ralentit l'application. Par conséquent, vous ne voulez pas que comme une généralité. Au lieu de cela, vous voulez que certaines de vos méthodes vérifient ses entrées. Cela peut être facilement fait avec des affirmations simples, ou avec des décorateurs si vous (à tort) pensez que vous en avez besoin beaucoup.

il existe également une alternative au contrôle de type statique, qui consiste à utiliser un élément orienté vers l'aspect architecture comme L'Architecture Zope Component. Au lieu de vérifier le type, vous adapter. Donc au lieu de:

assert isinstance(theobject, myclass)

vous faites cela:

theobject = IMyClass(theobject)

si theobject implémente déjà IMyClass, rien ne se passe. Si ce n'est pas le cas, un adaptateur qui enroule tout ce que theobject est à IMyClass sera recherché, et utilisé à la place de theobject. Si aucun adaptateur n'est trouvé, vous obtiendrez une erreur.

cela combinait le dynamisme de Python avec le désir d'avoir un type spécifique d'une manière spécifique.

12
répondu Lennart Regebro 2009-08-14 05:01:33

ce n'est pas une réponse à une question directe, mais j'ai trouvé une fourchette de Python qui ajoute le typage statique: mypy-lang.org , bien sûr on ne peut pas s'y fier car c'est encore un petit effort, mais intéressant.

12
répondu Ciantic 2013-02-11 15:05:33

bien sûr, la dactylographie statique semble un peu "Non rythmée" et je ne l'utilise pas tout le temps. Mais il y a des cas (par exemple des classes imbriquées, comme dans l'analyse de langage spécifique à un domaine) où cela peut vraiment accélérer votre développement.

alors je préfère utiliser beartype expliqué dans ce post *. Il est livré avec un git repo, des tests et une explication de ce qu'il peut faire et de ce qu'il ne peut pas faire ... et j'aime bien le nom ;)

* S'il vous plaît, ne faites pas attention aux railleries de Cecil sur les raisons pour lesquelles Python ne vient pas avec des piles incluses dans ce cas.

0
répondu Iwan LD 2017-05-23 11:54:06