Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

Действительно, создать универсальный принудительный редактор аннотаций в 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 и примените эту проверку с помощью аннотаций функций, я думаю, что это была бы проверка типов, которую люди действительно могли бы использовать!

Другие советы

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

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

Редактировать:

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

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

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

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

assert isinstance(theobject, myclass)

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

theobject = IMyClass(theobject)

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top