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

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

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

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

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

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

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.

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

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


Как упоминалось в этом PEP, статическая проверка типов является одним из возможных приложений, для которых могут использоваться аннотации функций, но они оставляют это сторонним библиотекам, чтобы решить, как это сделать. То есть, в core python не будет официальной реализации.

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

EDIT:

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


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

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

assert isinstance(theobject, myclass)

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

theobject = IMyClass(theobject)

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

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


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


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

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

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