Pergunta

Eu realmente não prestar tanta atenção ao desenvolvimento do Python 3 como eu teria gostado, e só notei algumas interessantes novas mudanças da sintaxe. Especificamente a partir esta resposta SO anotação parâmetro de função:

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

Não sabendo nada sobre isso, eu pensei que talvez pudesse ser usado para implementar tipagem estática em Python!

Depois de algumas pesquisas, parecia haver uma discussão muito sobre (totalmente opcional) tipagem estática em Python, como a mencionada em PEP 3107 e "Adicionando Opcional estática digitando para Python" (e parte 2 )

.. mas, eu não sou claro o quão longe este progrediu. Existem quaisquer implementações de tipagem estática, usando o parâmetro de anotação? Será que nenhuma das idéias do tipo parametrizado fazê-lo em Python 3?

Foi útil?

Solução

Obrigado por ler o meu código!

Na verdade, não é difícil para criar um aplicador da anotação genérica em Python. Aqui é a minha opinião:

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

Dada esta simplicidade, é estranho à primeira vista que esta coisa não é mainstream. No entanto, eu acredito que há boas razões para que a sua não é tão útil como poderia parecer . Geralmente, a verificação de tipo ajuda porque se você adicionar inteiro e dicionário, as chances são que você fez algum erro óbvio (e se você quis dizer algo razoável, ainda é melhor ser explícito do que implícito ).

Mas na vida real muitas vezes você misturar quantidades do mesmo tipo de computador como visto pelo compilador mas claramente diferente tipo humano , por exemplo, o seguinte trecho contém um erro óbvio:

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???

Qualquer ser humano deve ver imediatamente um erro na linha acima, desde que conhece o 'tipo humano' de variáveis ??height e length mesmo que pareça ao computador como o perfeitamente legal multiplicação de int e float.

Há mais que pode ser dito sobre as possíveis soluções para este problema, mas impondo 'tipos de computador' é aparentemente uma meia-solução, então, pelo menos na minha opinião, é pior do que nenhuma solução . É a mesma razão pela qual Sistemas Húngaro é uma idéia terrível, enquanto Aplicativos Húngaro é um grande. Há mais no muito informativo cargo de Joel Spolsky .

Agora, se alguém foi implementar algum tipo de Pythonic biblioteca de terceiros que atribuir automaticamente aos dados do mundo real a sua tipo humano e, em seguida, teve o cuidado de transformar esse tipo como width * height -> area e aplicar esse cheque com anotações de função, eu acho que seria um tipo de pessoas que fará poderia realmente usar!

Outras dicas

Como mencionado em que PEP, verificação de tipo estático é uma das possíveis aplicações que as anotações de função podem ser utilizados para, mas eles estão deixando para bibliotecas de terceiros para decidir como fazê-lo. Ou seja, não vai ser uma implementação oficial no núcleo python.

Quanto implementações de terceiros estão em causa, existem alguns trechos (como http: // code.activestate.com/recipes/572161/ ), que parecem fazer o trabalho muito bem.

EDIT:

Como uma nota, eu quero mencionar que a verificação comportamento é preferível a verificação de tipo, portanto eu acho typechecking estático não é tão grande uma idéia. Minha resposta acima destina-se a responder à pergunta, não porque eu faria typechecking-me de tal forma.

Isto não é uma resposta à pergunta diretamente, mas eu descobri um garfo Python que adiciona tipagem estática: mypy- lang.org , é claro que não se pode contar com ele como ele ainda é pequeno esforço, mas interessante.

"Static digitação" em Python só pode ser implementado de modo que o tipo de verificação é feito em tempo de execução, o que significa que retarda a aplicação. Portanto, você não quer isso como um generalidade. Em vez disso você quer alguns dos seus métodos para verificá-lo de entradas. Isso pode ser feito facilmente com simples afirma, ou com decoradores se (erroneamente) acha que precisa de muito.

Há também uma alternativa para verificação de tipo estático, e que é usar uma arquitetura de componentes orientada a aspectos como o Zope Component Architecture. Em vez de verificar o tipo, você adaptá-lo. Assim em vez de:

assert isinstance(theobject, myclass)

você fizer isso:

theobject = IMyClass(theobject)

Se theObject já implementa IMyClass nada acontece. Se isso não acontecer, um adaptador que envolve tudo o que theObject é a IMyClass será olhou para cima, e usado em vez de theObject. Se nenhum adaptador for encontrado, você receberá um erro.

Este combinou o dynamicism de Python com o desejo de ter um tipo específico de uma maneira específica.

Claro, tipagem estática parece um pouco "unpythonic" e eu não usá-lo o tempo todo. Mas há casos (aulas por exemplo aninhadas, como no domínio de análise linguagem específica), onde ele pode realmente acelerar o seu desenvolvimento.

Então eu prefiro usar beartype explicado nesta post *. Ele vem com um repositório git, testes e uma explicação o que pode eo que não pode fazer ... e eu como o nome;)

* Por favor, não prestar atenção ao discurso de Cecil sobre por que Python não vem com pilhas incluídas neste caso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top