2009-08-14 3 views
53

Я не обращал особого внимания на развитие Python 3, как мне бы хотелось, и только что заметил некоторые интересные новые синтаксические изменения. В частности от this SO answer функции параметра аннотации:Python 3 и статическая типизация

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

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

После некоторых поисков, казалось, было много обсуждения относительно (полностью по желанию) статической типизации в Python, как, например, упомянутых в PEP 3107 и "Adding Optional Static Typing to Python"part 2)

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

ответ

31

Спасибо, что прочитали мой код!

Действительно, нетрудно создать общий исполнитель аннотации в 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.

Есть еще что можно сказать о возможных решениях этой проблемы, но применение «типов компьютеров», по-видимому, является полурешением, поэтому, по крайней мере, по моему мнению, это хуже, чем никакого решения вообще. Это та же самая причина, почему Systems Hungarian - ужасная идея, а Apps Hungarian - отличный вариант. Есть еще в самом информативном post of Joel Spolsky.

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

+0

И вот что я только что написал об опасностях сильной типизации: http://stackoverflow.com/questions/1251791/what-are-the-limits-of-type-checking-and-type-systems/1276675#1276675 –

+32

Что если боб должен был нарисовать один квадратный метр своей спальни красной краской для каждого модуля, который он импортирует? –

+2

Тогда вы должны использовать явный тип приведения, чтобы наказать Боба. Каждая система сильной типизации, будь то статическая или динамическая, имеет положения для типоразмеров: например. '' string '+ 5' запрещен, но '' string' + str (5) 'отлично. –

14

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

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

EDIT:

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

+2

Обратите внимание, что упомянутый фрагмент принудительно проверяет динамический тип, а не ** статический ** тип проверки. –

+1

@Andrea Zillio: но возможно, чтобы IDE или скрипты могли проверить исходный код перед его выполнением (статическим), когда дана аннотация. Подумайте об def f (x: int) -> int, и кто-то пытается сделать f ('test'). – Joschua

+0

@Joschua: К сожалению, я не вижу, как IDE прыгают на борт с любыми неофициальными решениями. – Timmmm

12

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

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

assert isinstance(theobject, myclass) 

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

theobject = IMyClass(theobject) 

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

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

+5

Аннотации могут использоваться инструментами статического анализа, такими как pylint, для выполнения некоторой проверки на здравомыслие, очень похожей на статическую типизацию внутри областей кода, где тип переменной или параметра является познаваемым, а аннотации существуют на вызываемых API. – gps

+0

@ LennartRegebro: Это блестящая, простая идея. Я борюсь на каждом языке с проблемой «непустой строки».Большинство методов, принимающих строки в качестве параметров, недостаточно хорошо подготовлены для пустых строк или всех пробельных строк. В большинстве случаев это недопустимые значения, но немногие программисты должны его проверить. Мне нравится ваша идея использовать тип делегирования (псевдо) для применения ограничений типа/значения * один раз *: во время построения. Пример: 'str' ->' NonEmptyStr' или 'Text' (что подразумевает непустое и не все пробелы). – kevinarpe

+0

Да, но поскольку вы все равно можете пройти в пустой строке, это на самом деле ничего не решает. Если вы * также * не проверяете тип функции, а затем псевдо-тип фактически ничего не добавляет. –

12

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

0

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

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

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

Смежные вопросы