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

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

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

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

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

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


person dbr    schedule 14.08.2009    source источник


Ответы (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.

О возможных решениях этой проблемы можно сказать больше, но применение «компьютерных типов», по-видимому, является половинным решением, так что, по крайней мере, на мой взгляд, это хуже, чем полное отсутствие решения. По этой же причине Systems Hungarian - ужасная идея, а Apps Hungarian - отличная идея. Больше информации можно найти в очень информативном сообщении Джоэла Спольски.

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

person ilya n.    schedule 14.08.2009
comment
И вот что я только что написал об опасностях строгой типизации: stackoverflow.com/questions/1251791/ - person ilya n.; 14.08.2009
comment
Что, если Бобу пришлось бы красить один квадратный метр своей спальни красной краской для каждого модуля, который он импортирует? - person Otto Allmendinger; 14.08.2009
comment
Затем вы должны использовать явное приведение типа, чтобы наказать Боба. Каждая система строгой типизации, статическая или динамическая, имеет положения для приведения типов: например, 'string' + 5 запрещено, но 'string' + str(5) в порядке. - person ilya n.; 14.08.2009
comment
Также обратите внимание, что ваш пример будет просто sq.m * number = sq.m, что действительно имеет больше смысла, чем мой пример m * number = sq.m ??? - person ilya n.; 14.08.2009
comment
Кто-нибудь задумывался о том, насколько ограниченным должно быть завершение кода в IDE на Python, поскольку мы не знаем, какой тип переменной с именем a, мы не можем понять, что делать, когда пользователь вводит a. и нажимает ctrl-space .. . - person Warren P; 14.10.2011
comment
Я не понимаю, как можно перейти от этого решения, не решает каждую проблему. Это решение хуже, чем ничего. Если вы действительно думали, что вы даже не будете использовать python, потому что python не идеальный язык и, следовательно, хуже, чем ничего. Даже в правильных языках со статической типизацией семантические типы не применяются, потому что это невозможная проблема. Обычно они даже не применяют физические типы SI, но я думаю, это потому, что обычно это не стоит усилий (но есть системы, которые это делают). - person Timmmm; 23.08.2012
comment
@Timmmm, Илья не говорит, что все несовершенные решения хуже, чем ничего, просто это одно, потому что его обратная сторона (добавление сложности к и без того сложной задаче программирования) больше, чем ее положительная сторона ( это предохраняет вас от редкой ошибки, которую вы, вероятно, все равно не делаете). Философия Python ценит простоту - новая функция должна быть достаточно полезной, чтобы оправдать сложность, добавленную самим ее существованием. (Если бы в язык были добавлены все функции, которые могут быть кому-то полезны, полученный язык стал бы непригодным для использования.) - person Josh; 10.02.2013
comment
Он сказал, что это половинчатое решение, поэтому это хуже, чем отсутствие решения. Возможно, он имел в виду и не так, но, как написано, он говорит, что все полурешения хуже, чем никакие решения, что явно неверно. Но в любом случае ошибки типа - не редкость в больших проектах с динамической типизацией. А предотвращение ошибок - лишь одно из преимуществ статической типизации. Есть и другие важные преимущества, а именно автозавершение кода, рефакторинг и статический анализ. Я согласен, что это добавляет сложности, поэтому я начинаю думать, что дополнительная типизация Дарта может быть не такой безумной, как кажется на первый взгляд. - person Timmmm; 12.02.2013
comment
Рекомендую написать что-нибудь сильно разветвленное, работающее в одном потоке. Как компилятор с оптимизатором умеренно сложного языка или анализ, который выполняется на 2 ГБ данных с кубическим временем. Этот очень быстрый (за несколько дней постоянной отладки) может убедить, насколько бесполезны типы. - В частности, я могу похвастаться четырьмя вариантами написания и целой неделей запуска и отладки кода. - person Clare; 21.05.2013
comment
Один момент для проверки статического типа, например, в Boo, который имеет синтаксис Python, заключается в том, что проверки времени выполнения преобразуются на компилируемые. Хотя хороший код. - person Martin Tapp; 16.11.2013

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

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

РЕДАКТИРОВАТЬ:

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

person sykora    schedule 14.08.2009
comment
Обратите внимание, что в упомянутом фрагменте кода применяется проверка динамического типа, а не статическая проверка типа. - person Andrea Zilio; 17.02.2010
comment
@Andrea Zillio: но IDE или скрипты могут проверять исходный код перед его выполнением (статический), когда дается аннотация. Подумайте о def f (x: int) - ›int, и кто-то пытается сделать f ('test'). - person Joschua; 20.08.2011
comment
@Joschua: К сожалению, я не вижу, чтобы IDE использовали какие-либо неофициальные решения. - person Timmmm; 23.08.2012
comment
@sykora некоторые системы статического типа проверяют поведение. Вы просто делаете различие между номинальной и структурной типизацией. Проверьте объектную систему OCaml на наличие системы статического типа, ориентированной на статическое поведение. - person raph.amiard; 13.01.2014

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

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

assert isinstance(theobject, myclass)

ты делаешь это:

theobject = IMyClass(theobject)

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

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

person Lennart Regebro    schedule 14.08.2009
comment
Аннотации могут использоваться инструментами статического анализа, такими как pylint, для выполнения некоторой проверки работоспособности, очень похожей на статическую типизацию в тех областях кода, где известен тип переменной или параметра и есть аннотации для вызываемых API. - person gps; 01.04.2012
comment
@LennartRegebro: Это гениальная простая идея. Я борюсь на каждом языке с проблемой непустой строки. Большинство методов, принимающих строки в качестве параметров, плохо подготовлены для пустых строк или всех строк с пробелами. В большинстве случаев это недопустимые значения, но немногие программисты заботятся об этом. Мне нравится ваша идея использования делегирующего (псевдо) типа для применения ограничений типа / значения один раз: во время построения. Пример: str - ›NonEmptyStr или Text (что подразумевает непустые и не все пробелы). - person kevinarpe; 15.11.2014
comment
Да, но поскольку вы все еще можете передать пустую строку, это на самом деле ничего не решает. Если вы не также наберете проверку в функции, а затем псевдотип фактически ничего не добавляет. - person Lennart Regebro; 17.11.2014
comment
Разве assert не замедлит ваше приложение так же, как статическая типизация? Кроме того, мне интересно, как статическая типизация замедляет работу Python, когда все самые быстрые языки (c, c ++, java) имеют статическую типизацию. - person qed; 09.12.2014
comment
@qed: Это именно то, что я сказал. Если вы хотите иметь проверку типов на динамическом языке, таком как Python, это замедлит работу, потому что это выполняется во время выполнения. Маловероятно, что это будет иметь существенное значение, если вы не сделаете это в тесном цикле. Но будет медленнее, даже если незначительно. Настоящая статическая типизация не медленнее, потому что она выполняется во время компиляции. - person Lennart Regebro; 09.12.2014
comment
Хороший. Но у меня есть еще один вопрос. Разве динамическая типизация не включает в себя хотя бы какую-то форму проверки типа или определения типа? В любом случае, на это нужно время? Мне просто интересно, почему статическая типизация не поможет улучшить производительность (в списке рассылки разработчиков было некоторое обсуждение возможностей добавления какой-либо формы аннотации типов). - person qed; 11.12.2014
comment
Что ж, да, когда вы смотрите, например, метод, вы сначала смотрите на экземпляр объекта, а затем вам нужно искать объекты, от которых вы наследуете, в соответствии с порядком разрешения метода. В Python это делается достаточно быстро, но на статически скомпилированном языке это делается во время компиляции. Так что это часть ускорения. Но вы не можете сделать это на динамическом языке, поскольку классы могут меняться во время выполнения. - person Lennart Regebro; 11.12.2014
comment
Насколько я понимаю, это неверный ответ. Аннотации типов ничего не делают во время выполнения в соответствии с PEP 484. Подсказки типа существуют, чтобы такие вещи, как mypy, могли выполнять статическую проверку типа, чего не происходит во время выполнения. - person qfwfq; 10.04.2017
comment
Я никогда не утверждаю, что Python имеет статическую проверку типов во время выполнения. Я говорю, что если вы ХОТИТЕ это, вы должны реализовать это в своем приложении. - person Lennart Regebro; 17.04.2017

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

person Ciantic    schedule 11.02.2013

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

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

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

person Iwan LD    schedule 25.08.2016