Question

Je ne prêtais pas vraiment autant d'attention au développement de Python 3 comme je l'aurais aimé, et juste remarqué des nouveautés de syntaxe intéressantes. Plus précisément de cette réponse SO annotation de paramètre de fonction:

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

Ne sachant pas quoi que ce soit à ce sujet, je pensais qu'il pourrait peut-être utilisé pour la mise en œuvre typage statique en Python!

Après quelques recherches, il semble y avoir une discussion beaucoup au sujet (entièrement en option) typage statique en Python, tel que celui mentionné dans PEP 3107 et "Ajout statique en option Saisie à Python" (et partie 2 )

.. mais je ne suis pas clair dans quelle mesure cela a progressé. Y at-il des implémentations de typage statique, en utilisant le paramètre annotation? Est-ce que l'une des idées de type paramétrés en faire Python 3?

Était-ce utile?

La solution

Merci d'avoir lu mon code!

En effet, il est difficile de ne pas créer un enforcer d'annotation générique en Python. Voici mon:

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

Compte tenu de cette simplicité, il est étrange à première vue que cette chose n'est pas courant. Cependant, je crois qu'il ya de bonnes raisons pour lesquelles il est pas aussi utile que cela puisse paraître . En règle générale, la vérification de type aide parce que si vous ajoutez entier et le dictionnaire, il y a des chances que vous avez fait une erreur évidente (et si vous vouliez dire quelque chose de raisonnable, il est toujours mieux être explicite qu'implicite ).

Mais dans la vie réelle vous mélangez souvent des quantités de même type d'ordinateur comme on le voit par le compilateur, mais bien 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 être humain doit immédiatement voir une erreur dans la ligne ci-dessus à condition qu'il connaît le « type humain » des variables height et length même si elle semble à l'ordinateur comme parfaitement légal multiplication des int et float.

Il y a plus que l'on peut dire sur les solutions possibles à ce problème, mais l'application de « types d'ordinateurs » est apparemment une demi-solution, donc, du moins à mon avis, il est pire que pas de solution du tout . C'est la même raison pour laquelle Systèmes hongrois est une idée terrible alors que Applications hongrois est un grand. Il y a plus au très instructif poste de Joel Spolsky .

Maintenant, si quelqu'un était de mettre en œuvre une sorte de bibliothèque tierce Pythonic qui attribue automatiquement aux données du monde réel de son type humain , puis a pris soin de transformer ce type comme width * height -> area et d'appliquer ce contrôle avec des annotations de la fonction, je pense que ce serait un type de contrôle des gens a vraiment besoin!

Autres conseils

Comme indiqué dans ce PEP, la vérification de type statique est l'une des applications possibles qui fonctionnent annotations peuvent être utilisées pour, mais ils sont en laissant aux bibliothèques tierces de décider comment le faire. Autrement dit, il ne va pas être une mise en œuvre officielle en python de base.

En ce qui concerne les implémentations tiers sont concernés, il y a quelques extraits (tels que http: // code.activestate.com/recipes/572161/ ), qui semblent faire le travail assez bien.

EDIT:

Comme une note, je tiens à mentionner que le comportement de vérification est préférable de vérifier le type, donc je pense que statique n'est pas typage si grande idée. Ma réponse ci-dessus vise à répondre à la question, non pas parce que je me faire de telle vérification du type chemin.

Ce n'est pas une réponse à la question directement, mais je l'ai trouvé sur une fourchette de Python qui ajoute typage statique: mypy- lang.org , bien sûr on ne peut pas compter sur lui car il est encore petit effort, mais intéressant.

« Le typage statique » en Python ne peut être mis en œuvre afin que la vérification de type est effectué en exécution, ce qui signifie qu'il ralentit l'application. Par conséquent, vous ne voulez pas que comme généralité. Au lieu de cela que vous voulez certaines de vos méthodes pour vérifier ses entrées. Ceci peut être facilement fait avec plaine affirme, ou avec des décorateurs si vous (à tort) pense que vous devez beaucoup.

Il y a aussi une alternative à un contrôle de type statique, et qui est d'utiliser une architecture de composants orientée aspect comme Zope Component Architecture. Au lieu de vérifier le type, vous adaptez. Ainsi, au lieu de:

assert isinstance(theobject, myclass)

vous faites ceci:

theobject = IMyClass(theobject)

Si theObject implémente déjà rien ne se passe IMyClass. Dans le cas contraire, un adaptateur qui enveloppe tout theObject est à IMyClass sera examinée, et à la place de theObject. Si aucun adaptateur se trouve, vous obtenez une erreur.

Ceci, combiné au dynamicism de Python avec le désir d'avoir un type spécifique d'une manière spécifique.

Bien sûr, le typage statique semble un peu « unpythonic » et je ne l'utilise pas tout le temps. Mais il y a des cas (classes imbriquées par exemple, comme dans le domaine de l'analyse syntaxique de langage spécifique) où il peut vraiment accélérer votre développement.

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

* S'il vous plaît ne faites pas attention à la diatribe de Cecil raisons pour lesquelles Python ne vient pas avec des piles incluses dans ce cas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top