В Python 3 и статическая типизация


Я действительно не уделял столько внимания развитию Python 3, как мне бы хотелось, и только что заметил некоторые интересные новые изменения синтаксиса. Конкретно из этого так ответьте на аннотацию параметра функции:

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

ничего не зная об этом, я подумал, что это может быть использовано для реализации статического ввода в Python!

после некоторого поиска, казалось, было много дискуссий относительно (полностью необязательного) статического ввода в Python, таких как что упоминается в PEP 3107 и "добавление дополнительного статического ввода в Python"часть 2)

..но, я не понимаю, как далеко это продвинулось. Существуют ли какие-либо реализации статической типизации, используя параметр-аннотацию? Были ли какие-либо идеи параметризованного типа в Python 3?

5 54

5 ответов:

Спасибо за чтение моего кода!

в самом деле, это не трудно создать универсальный подавитель аннотация в Python. Вот мое мнение:

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

учитывая эту простоту, странно на первый взгляд, что эта вещь не является мейнстримом. Тем не менее, я считаю, что есть веские причины, почему это не так полезно, как может показаться. Как правило, проверка типа помогает, потому что если вы добавляете целое число и словарь, скорее всего, вы сделали какую-то очевидную ошибку (и если вы имели в виду что-то разумное, это все равно лучше быть явным, чем неявное).

но в реальной жизни вы часто смешиваете количества одного и того же компьютер типа как видно компилятором, но явно разные тип человека, например следующий фрагмент содержит очевидную ошибку:

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

любой человек должен немедленно увидеть ошибку в приведенной выше строке, если он знает "человеческий тип" переменных height и length хотя это выглядит для компьютера Как совершенно законно умножение int и float.

есть больше, что можно сказать о возможных решениях этой проблемы, но применение "компьютерных типов", по-видимому, является половинчатым решением, поэтому, по крайней мере, на мой взгляд, это хуже, чем вообще никакого решения. Это та же причина, почему Венгерский Систем это ужасная идея в то время как Приложения Венгерские большой. Там больше на самом деле информативно пост Джоэла Спольски.

теперь, если кто-то должен был реализовать какую-то стороннюю библиотеку Pythonic, которая автоматически присваивала бы реальным данным ее тип человека а затем позаботился о том, чтобы преобразовать этот тип, как width * height -> area и примените эту проверку с аннотациями функций, я думаю, что это будет проверка типа, которую люди действительно могут использовать!

Что касается сторонних реализаций, есть некоторые фрагменты (например,http://code.activestate.com/recipes/572161/), которые, кажется, делают работу довольно что ж.

EDIT:

в качестве примечания я хочу упомянуть, что проверка поведения предпочтительнее проверки типа, поэтому я думаю, что статическая проверка типа-это не такая уж отличная идея. Мой ответ выше направлен на ответ на вопрос, а не потому, что я бы сделал проверку типа сам таким образом.

"статическая типизация" в Python может быть реализована только так, что проверка типа выполняется во время выполнения, что означает, что она замедляет работу приложения. Поэтому вы не хотите, чтобы это было общностью. Вместо этого вы хотите, чтобы некоторые из ваших методов, чтобы проверить его входы. Это можно легко сделать с помощью простых утверждений или с декораторами, если вы (ошибочно) думаете, что вам это нужно.

существует также альтернатива статической проверке типов, а именно использование архитектуры аспектно-ориентированных компонентов как архитектура компонентов Zope. Вместо того, чтобы проверять тип, вы адаптируете его. Так что вместо:

assert isinstance(theobject, myclass)

вы делаете это:

theobject = IMyClass(theobject)

Если theobject уже реализует IMyClass ничего не происходит. Если это не так, адаптер, который обертывает любой объект theobject в IMyClass, будет просмотрен и использован вместо объекта theobject. Если адаптер не найден, вы получите сообщение об ошибке.

это сочетало динамизм Python с желанием иметь определенный тип в конкретном путь.

Это не ответ на вопрос напрямую, но я обнаружил вилку Python, которая добавляет статический ввод:mypy-lang.org, конечно, на это нельзя полагаться, так как это еще маленькое начинание, но интересное.

уверен, статическая типизация, кажется, немного "unpythonic" и я не использую его все время. Но есть случаи (например, вложенные классы, как в разборе конкретного языка домена), где он действительно может ускорить вашу разработку.

тогда я предпочитаю использовать beartype пояснил в этой post*. Он поставляется с РЕПО git, тестами и объяснением того, что он может и чего он не может сделать ... и мне нравится название ;)

* пожалуйста, не обращайте внимания на разглагольствования Сесила о том, почему Python не поставляется с батареями, включенными в этот случай.