Когда Гвидо ван Россум впервые написал, что он подумывает о том, чтобы попробовать включить в Питон опциональное описание типов аргументов и значений функций, а потом долго гасил флейм, я сразу эмоционально принял сторону ярых противников этой штуки. В конце концов, у нас же есть duck typing! И всякая идея декларировать тип объекта отдельно от того, что он и так сам собой представляет, ведет ко всем неудобствам сидения на двух стульях и выполнению двойной работы. И вообще — ересь какая-то :-)
Однако в сегодняшнем его посте о подготовке Питона 3000, я узнал, что решение о включении этой опциональной типизации (которая называется "function annotations") таки принято.
Тем не менее, вместо того, чтобы идти в угол плакать, я решил все же прочитать, что же оно на самом деле собой представляет: PEP 3107, Function Annotations. Все, оказывается, гораздо веселей, чем можно было думать! Для тех, кто не пойдет читать (знаю, сам такой :-) ), перескажу вкратце, что мне больше всего понравилось.
"Аннотации" — это выражения, которые как-то описывают параметры и возвращаемое значение функции, типа такого:
def func(a: Integer, b: String) -> Sequence:
# ...
Но это не типы!
Во-первых, они не просто опциональны, а совсем опциональны. То есть, их не только не обязательно указывать, как таковые, но и даже если они указаны, в функцию все равно можно передавать все что угодно, как и раньше. Их роль — исключительно добавочная, для того, чтобы всякие сторонние тулзы могли делать больше предположений о коде и предоставлять лучший сервис. Речь идет о дополнении кода и подсказках в редакторе IDE, внешних проверяльщиках корректности кода и т.д. Цитируя PEP, аннотации "не имеют никакого собственного смысла; смысл определяется только сторонней библиотекой".
Во-вторых (после чего я теперь считаю, что аннотации — вещь!), они не обязаны быть похожими на типы. Это может быть любое выражение: число, строка, вычисление, ну и тип тоже, конечно. Можно например написать:
def show(f: 'Filename or a file-like object'):
# ...
И аннотацией к параметру "f" будет просто поясняющий текст. Также, насколько я понимаю, можно сказать "возвращаемое значение функции "process" предполагается таким же, как у функции "prepare":
def process() -> prepare.func_annotations['return']:
# ...
Короче говоря, это не какая-то страшная "типизация", это просто такая структурированная документация, дающая полезные подсказки сторонним тулзам.
Кстати, первая альфа Питона 3000 обещается в этом августе. А релиз — в следующем августе. Пора начинать привыкать :-).
Комментарии: 38
Это правильно. Я, например, начав разбираться с Django и Питоном, был неприятно удивлен тем, что не могу понять, что делает метод, что от него ожидать и чего в него можно передать, просто глянув на заголовок. Все таки строгая типизация и variant там, где нужно - это самый правильный подход IMHO.
В мире С, C++ Java, PHP и т.п. для этих целей используется формат Doxygen (http://www.stack.nl/~dimitri/doxygen/)). Кстати, на их сайте заявлена поддержка и питона ;).
Что заставляет придумывать новый подход и отказываться от проверенного решения?
Основная мотивация "почему не docstring'и", насколько я понял, в том, что docstring'и тогда получатся слишком сложными и слишком для многих вещей. С аннотациями проще работать — их не надо парсить отдельно. А сейчас да, именно в докстрингах все такое и принято хранить.
ЧТо значит "не надо парсить отдельно"? Как я понял этим занимаются редакторы и IDE, а Pyton'овский парсер их будет просто игнорировать. А для IDE особой разницы нет что аналицировать аннотации или Doxygen. Кроме того, формат Doxygen позволяет позволяет описать работу метода, в большинстве случаев простого указания типов и кратиких описаний параметров и возвращаемого результата будет не достаточно.
2dacuan.
Питоновский парсер их не будет игнорировать, он их будет засовывать в метаданные объекта. Примерно так же как сейчас он хранит докстринги в __doc__. То есть сам Python не будет делать никаких выводов из аннотаций, но будет их хранить структурированно. И дополнительно парсить докстринги, чтобы выдернуть из них информацию о параметре, не потребуется.
Когда прочитал PEP, сама идея мне очень понравилась. С другой стороны, это лишь отличная потенциальная возможноть. Реальная польза будет целиком и полностью зависеть от тех самых третьесторонних библиотек, которые эти аннотации должны использовать. В Python 3.0, и вероятно, в несколько следующих версий тоже, ни одна из таких библиотек не войдет.
doxygen может и неплох для си, но для питона лучче epydoc
@Максим
т.е., доводя до конечной точки можно сказать, что аннотации == докстринги для параметров функций. Что правильно. Отдельное спасибо Ивану за разъяснение.
Alex Lebedev:
Если доделают то войдет библиотека для перегрузки
ф-ций, так-же есть вполне устоявшаяся typechecker,
ее то-же скорее всего перепишут. Кроме этого можно
ожидать существенного улучшения автодополнения в IDE. И упрощений/улучшений в PyREX,shedskin,etc.
Мне кажется, это ужасная тенденция. В php эту недотипизацию вставили, в js собираются, теперь вот до питона дошли. И везде никакого практического смысла в ней нет, только раздувание кода и введение в язык новых сущностей. Нафига type hinting и комментарии к параметрам, если все IDE давно понимают докстринги?
Для Давида Мзареуляня:
помоему Вы не дочитали или неправильно поняли что
это такое. Это не типизация - типизация не вписывается в duck typing, это не "док стринги" -
их не надо парсить, это готовые данные. Питону катастрофически нужна опциональная типизация - это
путь к решению многих проблем ( от оптимизации до упрощения написания инструментов типа pychecker ). Единственна IMHO проблема (точнее палка о двух
концах) это отсутствие хоть каких-нибудь
стандартов на аннотации - может привести к
маленькому хаосу.
Да всё я дочитал. Почему Вы думаете, что "Питону катастрофически нужна опциональная типизация"? Что такого "катастрофического" без неё происходит, что потребовалось менять синтаксис языка?
Я уж не говорю о том, что Питон, вообще-то, и так строго типизированный язык. Так, на минуточку. Если кто забыл.
Тогда предлагаю прочитать еще обсуждение с
pydev рассылки и PEP соответствующий
http://www.python.org/dev/peps/pep-3107/
, что-бы подобные вопросы не возникали, там все расписано.
Это динамическая типизация. Я уже описывал раньше утилиты/библиотеки которые сильно
выиграют от статической типизации. Вот расширенное описание возможностей по оптимизации - будет возможна прозрачная статическая компиляция части кода инструментами типа pyrex. Очеь большая часть кода, который я пишу - статически типизированная.
И иногда после профилирования приходится его переписывать на C и Pyrex'ом связывать с питоном.
После введения аннотирования достаточно будет указать типы в такой ф-ции + добавить к ней декоратор - все остальное можно будет автоматизировать. То-же относится и к psyco - его работа существенно упростится, т.к. все что ему не хватает - это типов. Аналогично shedskin.
И еще вагончик либ типа scipy.weave.
То есть теперь получается, что если я захочу скорости на родном языке, мне надо будет всего лишь указать у критичных участков кода аннотации, декоратор и скорость взлетит? О счастье, холиварщики повоюют и сами станут пользоваться : )
to Deepwalker.
Я поправлюсь, на всякий случай. Это только
потенциальная возможность, т.е. ничего
подобного по-умолчанию встроено в питон не будет.
Хотя ее реализация весьма простая, и я вполне
представляю себе как это можно сделать, т.е. если
это не сделают за меня, то я сделаю это сам )).
Интересно, а что прикрутят к этому джанговцы? Они же достаточно активно используют возможности python в части интроспекции.
Нашел еще одно интересное(IMO) применение комбинации декораторов для классов и аннотаций.
Автоматическая перегрузка + сопоставление с образцом (то чем так гордятся любители Haskell и остальной функциональщины). Т.е. можно будет сделать так:
2 koder
Такая штука будет и без аннотаций и декораторов, называется generic functions
Максим Деркачев:
Generic functions это то что будет
сидеть "under the hood"(ключевое слово
моего поста было "Автоматическая").
Для generic functions нужно явно
декораторами(overload или when) отмечать
перегрузку для каждой перегружаемой
ф-ции(http://www.python.org/dev/peps/pep-3124/),
а за счет новых метаклассов это станет не
нужно. when же будет основой для сопоставление с
образцом. А аннотации нужны в любом случае -
именно из них generic и вынимают данные о
перегрузке и специализации.
P.S. Спасибо Ивану за правку кода в моем посте.
Возможно это by design. Свобода маневра чтобы дать best practices сформироваться.
Документации никогда не бывает много. Особенно если она является дополнительной и более структурированной, чем предыдущая.
Описание параметров будет очень полезным для работы сторонних (уже существующих) утилит - скажем, для статической типизации (опциональной, между прочим).
Кстати - если та же статическая типизация через аннотации будет востребована и станет популярной, то она рано или поздно появится в Python. Может, не в Python 3.5, а в Python 4000 ;)
Что автоматически а) поставит крест на свободе комментирования (утилитам-то конкретный формат подавай) и б) расплодит зоопарк этих самых форматов. И будем потом в затылке чесать, почему код, написанный в одном IDE не подхватывается в другом.
Вот честно пытаюсь хоть один плюс в этом найти — не могу.
Лучше бы они вместо строчек, lambda добавили. Например вот так:
def divide(a, b: b != 0):
return a / b
Давид Мзареулян написал:
Вы повторяете то, что я уже написал выше.
PEP мы так и не прочитали 8(. Не используйте
аннотацию - никто не заставляет.
Василий написал:
Так сойдет? Выражение компилируется при первом
обращении к функции.
Он хотел, чтобы проверялось b : ))
Напишите себе декоратор - пусть проверяет.
В том то и суть, что использование не ограничено.
было бы очень интересно если бы в питоне добавили возможность подключать/отключать статическую типизацию.
нужно написать быстро - пожалуйста используй динамическую типизацию, а нужно чтобы быстрее работала - статическая типизация.
Могу посоветовать посмотреть на PyPy и его Restricted Python. В принципе, действительно несколько похоже на статическую типизацию, и если я не ошибаюсь, RPython транслируется в низкоуровневые языки, и тогда всё красиво и быстро. А ограничений там кот наплакал.
http://codespeak.net/pypy/dist/pypy/doc/coding-guide.html#restricted-python
to Deepwalker
Это подразумевалось ;) -
"Выражение компилируется при первом обращении к
функции.". Скорее удобнее будет сделать это на
классовых декораторах или сразу для всего модуля - что-бы не декорировать каждую ф-цию в отдельности.
Кстати то что подобное не используется сейчас
сразу показывает насколько оно нужно. Это скорее
ближе получается к "design by contract".
Murkt:
Из ограничений вся динамическая часть и интроспекция и стандартная библиотека и декораторы и .... ;). Ну да ладно, при компиляции это в любом случае не выживет. Основная проблема это невозможность "миксовать"(AFAIK) Python и RPython код, т.е. невозможно написать часть программы на RPython, а часть на Python и прозрачно вызывать ф-ции. IMHO удобнее PyReX + классы, выдранные из shedskin кода (хотя тоже так се).
Было бы отлично еслиб это введение помогло написать статический компилятор с поддержкой вывода типов. Иначе язык станет еще медленее, ибо все радости жизни вроде перегрузки функций/методов, сопоставления с образцом и тд, будут жрать ресурсы в ран-тайме.
Фигня это. Нет мануала лучше, чем несколько примеров вызова функции (посмотрите api.rubyonrails.com). А тут язык загромаждают мусором.
К сожалению, JIT-компиляторам, анализаторам, IDE недоступен высший дзен.
Murkt: мне один умный товарищ говорил, что код пишут для того, чтобы его читал человек и лишь иногда — машина.
Oleg Andreev: аннотации мешают восприятию кода?
Забавная идея, ничего не скажешь. Главное, конечно, не вздумать отойти от duck-typing, впрочем это поставит крест на всём написанном объёме кода =)