RUVDS.com corporate blog
Website development
Python
Comments 82
+5
Мне кажется, абсолютно необходимо упомянуть, что аннотации типов это не проверка типов, а подсказка компилятору.
>>> def f(a: str):
...     print(a)
...     return
... 
>>> f('a')
a
>>> f(1.0)
1.0
>>> import sys
>>> sys.version
'3.5.2 (default, Nov 12 2018, 13:43:14) \n[GCC 5.4.0 20160609]'
+3
Моя интуиция почему-то протестует против внесения в синтаксис языка фичей предназначеных для IDE. Слишком сильно связаны получаеются IDE и язык.
+1
а как комплиятор сейчас это использует? Насколько я читал, CPython пока никак не использует type annotation, а вот IDE уже вовсю
0
Использование аннотаций, помимо прочего, достаточно сильно повышает читаемость кода. Так что можно считать это не только фичей для IDE.
0
Есть еще анализатор mypy, который умеет проверять соответствие типов
0

Для произвольного кода?


Какова вообще выразительная сила тайпхинтов как системы типов?

0
Там есть Any, Union (sum type), дженерики и пр. Т.е. можно описать любой тип, даже сложную вложенную структуру или функцию. Можно создать свой кастомный тип.
В общем Python списывает хорошее у Haskell ещё начиная со списковых выражений.
подробнее здесь: docs.python.org/3/library/typing.html
0
дженерики

А там накладывать требования на типы можно? Ну, например, чтобы у меня дженерик был не поверх любого типа T, а поверх только U[T] для фиксированного (или нет) U?


В общем Python списывает хорошее у Haskell ещё начиная со списковых выражений.

Тайпклассы, экзистенциальные типы, кусочки зависимых типов, kind polymorphism?

0
чтобы у меня дженерик был не поверх любого типа T, а поверх только U[T] для фиксированного (или нет) U?

Кажется, да.

Много можно чего ещё скопировать из Haskell (и раздуться похлеще монстра C++), но задачи такой, как я понимаю, не стоит. Я просто имел в виду, что разработчики Python в сторону Haskell смотрят внимательно и заимствуются фичи оттуда давно, потому что list comprehensions в Python с 2000 года.
0
Много можно чего ещё скопировать из Haskell (и раздуться похлеще монстра C++)

Ну это вы зря так, сам язык (если на библиотеку не смотреть) вообще мелкий, меньше сотни страниц ЕМНИП.

0

Можно указать типы при создании TypeVar и будет он co-, contra- или in- вариантным. К сожалению, пока этот механизм в том же mypy и pycharm работает с проблемами

+8
Python это не компилируемый язык.
Код python исполняется интерпретатором, а он в свою очередь все эти аннотации типов полностью игнорирует, что вы же в своём примере и продемонстрировали.

Аннотации это для чего угодно, но не для исполнения кода: для читаемости, для подсказок в IDE, для статического анализа (mypy тот же самый), etc.
+1
Ну здравствуйте, вроде как всю жизнь код питон компилируется в промежуточный байт-код и этот байт код затем исполняется.
+1
Ну объектные файлы же не продуцируются. Слинковать с другими объектными файлами ничего нельзя.

Байт-код все равно исполняется виртуальной машиной.
0
С точки зрения типов, компилятор мог бы проверять типы на этапе компиляции байт кода. Таким образом, можно проверять код до его запуска. У чистого интерпретатора, без этапа компиляции такой возможности нет, в случае ошибки типа он все-равно должен закончить выполнение.
Т.е. у чистого интерпретатора мало смысла в проверке типов.
Я то сам, дне думаю, что для динамического языка не стоит внедрять обязательную проверку типов. Ну если только в качестве опции в виде билбиотеки. Не в ядре.
Я за то, чтобы сохранять ядро как можно более компактным.
0
С такой логикой любой язык можно назвать интерпретируемым, ведь в конечном итоге всё интерпретируется процессором.
0
При всём уважении к Вашему юзернэйму, это так не называется.
0
Не называется, но если следовать логике lorc (при которой если язык компилируется в нечто, что далее интерпретируется, то сам язык считается интерпретируемым), все языки можно назвать интерпретируемыми.
0
Нет, это не моя логика. Пожалуйста, не надо приписывать мне то, чего я не говорил.
-1
А это чьи слова:
Байт-код все равно исполняется виртуальной машиной.

И это в ответ на утверждение, что питон компилируется. Из контекста я понял, что вы считаете его интерпретируемым. Или я ошибаюсь?
+1
Да, я продолжаю считать питон интерпретируемым. И буду считать его таким, пока не смогу получить из программы на питоне объектный файл, скормить его ld и слинковать его с моей программой на С.
0
Из программы на питоне можно получить .pyc файл. И при этом ни строчки из этой программы не исполнить.
0
Скажите, .pyc-файл исполняется непосредственно на процессоре, без вспомогательных прокладок?

Вот когда начнёт исполняться — тогда и будет компилируемым.
0
Но байткод-то в результате чего получается? В результате компиляции питонокода.

Машкод тоже может не исполнятся непосредственно на процессоре. В современных процах он собственно и не исполняется, а транслируется в микрокоды, которые затем исполняются.
0
Кроме того, существуют реализации языков, которые компилируют исходный текст программы в байт-код, который затем либо интерпретируется, либо выполняется т. н. JIT-компилятором (или виртуальной машиной). Это привносит ещё больше неясности в вопрос о том, где именно должна быть проведена граница между компилируемым языком и языком интерпретируемым.


Можно и поспорить ;)
0
Нет. Компилятор производит объектные файлы, которые потом линкуются в исполняемый файл. Который потом может быть загружен в память и исполнен процессором. Это вроде бы обычный способ определять компилируемый язык.

И нет, если вы запакуете виртуальную машину и байт-код в один файл, то это все равно не будет скомпилированной программой. Ваша программа (а не ВМ) должна быть переведена в машинные коды, которые смогут исполняться процессором целевой платформы.
+1
Во-первых, компилятор не обязательно генерирует объектный файл, он может генерировать и файл с байт-кодом, или сразу исполняемый файл, или вообще простой бинарный файл являющийся сырым образом программы и её данных (ещё возможен вариант генерации программы на другом языке, но это принято называть транспилером).
Во-вторых, компилятор может вообще не генерировать никаких файлов, а компилировать программу в памяти, снаружи он будет подобен интерпретатору, но компилятором от этого быть не перестанет.

если вы запакуете виртуальную машину и байт-код в один файл, то это все равно не будет скомпилированной программой

Здрасьте, а байткод откуда взялся, если не был скомпилирован?

Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).
+1
>>Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).

После последней фразы могу запостить только эту ссылку. Финиш, конечно:
www.youtube.com/watch?v=LLk9_EH6Pfo
0
Эм в этом примере наоборот же получается, чистый питон игнорирует аннотации.
0
Ну да, я это и хотел показать. Что чистый питон не проверяет типы. Если бы проверял, он что-то бы сделал, например выбросил бы исключение.
0
Ну скажем так, это не настоящая проверка типов, но кое-где они проверяются. Например dataclass не создает параметр для __init__ у которого не проставлен тип данных или добавленная перегрузка overload, из того же пакета typing, вообще не работает. Очень надеюсь в будущих выпусках разработчики усилят это направление.
0
Для меня большой вопрос, нужно ли тащить статическую проверку типов в рантайм питона. Возможно просто я не сталкиваюсь с необходимостью статической проверки, но лично меня вполне устраивает дак тайпинг в питоне. Если нужна строгая типизация можно просто взять другой язык. Ну хотя бы тот же Cython. Или Си или C++.
Если все тащить в одну кучу получится еще один C++, точнее получим все недостатки C++, без его достоинств.
В моей практике ошибки с типами так редки, что ИМХО не стоит городить огород.
+1

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

+2
В виде подключаемых библиотек я готов мириться. Пусть будет, для любителей. Но только не в ядре языка.
По моему это просто мода. Лично я накушался строгой типизации в C++. А так сначала можно затащить строгую типизацию в питон, а потом без дизайн паттернс ни шагу ступить нельзя будет.
0
Без аннотации типов командой больше 5 человек разрабатывать очень трудно. Совершенно непонятно что тебе пробросили через 3 функции. Или могу я вызвать чужой метод и отправить в него set вместо list. это нужно еще и чужой код просмотреть прежде чем использовать.

Ну и уж совсем мечты это нормально предсказание типов для LLVM и для компилятора в WebAssemly, а то питон сейчас жив одним ML
+1
статическую проверку типов в рантайм

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

Если нужна строгая типизация можно просто взять другой язык

Типизация в питоне, кстати, строже чем в большинстве скриптовых. Хоть функции и принимают любые объекты, но вольностей, типа складывания строки с числом он не позволяет, и неявных приведений типов (как в js — лишь бы что-то выполнить) он не делает.
0
Хоть функции и принимают любые объекты, но вольностей, типа складывания строки с числом он не позволяет, и неявных приведений типов (как в js — лишь бы что-то выполнить) он не делает.

>>> 3 == "10"
False

Сравнивать значения разных типов он, однако, вполне себе позволяет.

+1
Потому что это осмысленная операция, и, в отличие от всяких JS и PHP, питон не будет пытаться привести их к одному типу, т.е. для разных типов всегда будет False.
0
Просвятите, какая необходимость иметь в вашем языке оператор ==?
0
Мой язык — русский, в нём нет этого оператора. Кто касается ПХП, то достаточно вспомнить историю языка — простая работа с вебом, данные из форм приходят нетипизированно.
0

Для разных типов должен кидаться экзепшон, потому что никакие два значения разных типов никогда не могут быть равны. Если я сравниваю значения разных типов, то у меня ошибка в логике. John Major equality — плохая штука.


Иными словами, False в 3 == 4 и False в 3 == "4" имеют принципиально разную семантику.

0
Это позволяет избежать лишних проверок типов руками. Обычное дело для функций, принимающих параметры различного типа.
0

А зачем параметры различного типа сравнивать? Они же различного типа.

0
С динамической типизацией тип параметров заранее может быть не известен же.
0

Со статической в каком-то смысле тоже не обязан. Обычное гомогенное равенство вполне себе полиморфно.

-1
>> 3 == "10"
False
Правильнее так:
>>> print( 10 == "10")
False

Если числа разные — непонятно, что есть False
Без print не работает
>>>  3 == "10"
  File "<stdin>", line 1
    3 == "10"
    ^
IndentationError: unexpected indent

+2
> Без print не работает

Конечно, если пробел впереди воткнуть — работать не будет.

>>> 3 == "10"
False
>>>  3 == "10"
  File "<stdin>", line 1
    3 == "10"
    ^
IndentationError: unexpected indent


только print тут ни при чём.
+1
Clojure — интерпретируемый язык с динамической типизацией, но там есть библиотека spec, которая позволяет описать «схему» данных примерно как в Python, но в рантайме это всё проверяется на корректность. И также есть аннотации типов, которые позволяют указать тип Java и это влияет на производительность, потому что можно написать алгоритм, который будет работать только со списком Long, не задействуя рефлексию.
0
Наверное лучше вообще убрать и компилятор и статический анализатор и просто написать; «это просто подсказки».
0

Также, type hints можно использовать, когда нам нужна явная привязка названия переменной к её типу. Например, есть библиотека pydantic, которая использует type hint'ы в датаклассах для маршаллинга и валидации схемы данных. Мы перешли на неё с marshmallow, очень удобно.

0

Стоит отметить, что pydantic использует свои классы вместо питоновских dataclasses, что может ограничивать применимость. Я в свободное время пилю библиотечку, которая может быть применима для уже существующих датаклассов без необходимости как-то их менять.

0
Python молодцы. Многие языки еще долго будут догонять.

Еще бы добавить аннотаций для функционального программирования и будет еще круче
-1
Повторюсь. Я за то, чтобы сохранять ядро языка как можно более компактным. Чтобы его можно бы было всюду использовать с минимальными изменениями.
+1
По идее давно напрашивается модульный язык. Зачастую в энтерпрайзе надо чтобы программисты использовали одни и те же конструкции и не использовали какие-то другие. Надо научить язык отсекать лишние в конкретном проекте конструкции.
0
В модуле typing вроде есть Callable? Т.е. функции высшего порядка вполне могут работать с этими аннотациями.
0
аннотаций для функционального программирования

Вы мне лучше скажите, какая аннотация должна быть у функции, принимающей совпадение по регулярке (тип _sre.SRE_Match)?
+2
Какие возможности Python 3 вы добавили бы в приведённый здесь список?
asyncio и холиварный := (3.8+)
0
что сказать, язык будущего))

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

и очень интересная фича LRU кэш, я чессно не знал и dict юзал
кста вроде одинакого по скорости этот кэш и ручной через dict, кто нибудь протестил нормально скорость??

имхо, в Питон версии 100500 + следует ввести:
  • инспекцию структуры любого обьекта с преттипринтом или лучше в визуальном представлении, когда берёшь неизвестный модуль, где документация плохая а все типы стёрты
  • обьединить нотацию для класса и dicta, inst['field'] эквивалентно inst.field

    интерпретировать сложные comprehention декларативно с оптимизаций и кешем где надо


    встроить без геморa частичные функции, с поддержкой интерпретатора заместо partial:

    def fn ( a,b,c)

    k = fn(1,2) #-> обьявление частичной функции k(c) c изсестными a,b,
    потом
    k[0] = 3 # безгеморная замена параметра на определённом месте




    сложные lambda обьявления с функционалом как обычный метод

    fib = lambda @lru_cache(maxsize=512), a: int ,b : int -> int : if number == 0: return 0
    if number == 1: return 1

    return fib_memoization(number-1) + fib_memoization(number-2)





0
кстати о f-string.
оно и раньше так же примерно работало
"Hello {user}!".format(**locals) 

или **vars
+1
Все же не так красиво, да и по скорости f-string в разы* быстрее:
Type            Trial 1                 Trial 2                 Trial 3
%:              0.273233943000          0.268914790000          0.273714235000
str.format():   0.7942503730000681      0.793637686999773       0.7926878570001463
str.Template(): 3.3321329630002765      3.3256752329998562      3.315622544999769
f-string:       0.1914799450000828      0.18900782099990465     0.19004946999984895
Тестировалось на python 3.7, оригинал.
0
оно и раньше так же примерно работало

Нет, не так же. Можно было только подставлять значения, а делать вычисления прямо внутри было нельзя (теперь в f-string можно). Так что теперь нужно быть осторожнее с такими строками. <sarkazm>Это почти как маленький php внутри python.</sarkazm>
Ко всему + то что сказал lega производительность в разы больше.


Примерные тесты
~ $ python3.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
500000 loops, best of 5: 620 nsec per loop
~ $ python3.7 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"'
2000000 loops, best of 5: 157 nsec per loop
~ $ python3.6 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
1000000 loops, best of 3: 0.599 usec per loop
~ $ python3.6 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"'
10000000 loops, best of 3: 0.142 usec per loop
~ $ python2.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
1000000 loops, best of 3: 0.435 usec per loop
0

Изменения крутые и полезные.
Очень хотелось бы что бы исправили концептуальные проблемы языка, даже если это означает python 4 и сломанную обратную совместимость.


Например те же импорты все ещё очень сложны для понимания новичками (особенно по сравнению с другими языками). При этом циркулярные импорты падают в рантайме с неадекватной ошибкой, а orm фреймворки вроде той же алхимии предлагают решать проблему просто — указывать название классов текстом *facepalm".

+1

К сожалению, я изучал питон очень давно и не могу 100% вспомнить какие у меня были проблемы. Но, кажется, импорты — это не та вещь, которая мне показалась непонятной.


Про циклические импорты действительно не мешало бы добавить текст более явный, хотя если посмотреть на стек это и глазами довольно легко понять.


В ОРМ циклические завимости надо решать не написанием названий классов текстом, а правильной расстановкой relation, мне кажется это в 95% случаев возможно (если я не прав — прошу привести пример).

0
циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки, зря что так не встраивают. Странное дело её не разруливает даже maven в своих проэктах…
+1

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

+2
циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки
Как такая цикличекая зависимость может решиться интерпретатором?
# a.py
import b
value = b.value + 5

# b.py
import a
value = a.value + 7

PS: В питоне нормально решаются циклические зависимости, проблемы не в питоне, а «в руках».
0
её не разруливает даже maven в своих проэктах

Имхо, с ней намного проще получить в проекте одну и ту же зависимость с разными версиями. И долго искать причину фокусов в рантайме.

0
LRU-кэш полезная штука, но надо пользоваться с умом, т.к. он подходит только для идемпотентных функций(методов)
+1
Если говорить о Python 3 в целом, то в версии 3.8 у нас будут Assignment Expressions (PEP 572):
if x := True:
     print(f'{x}')
# Выведет: True

x = 2
print([y := x**x, y**2, y**3])
# Выведет: [4, 16, 64]


И много разговоров про None-aware operators (PEP 505), значительно сокращающие код:
# Old
data = data if data is not None else []
# New
data = data ?? []

# Old
if lst:
    lst.append('string')
# New
lst?.append('string')

# Old
if callable(foo):
    foo()
# New
foo?()

# Old
if x:
   x + 1
# New
x? + 1

# Old
result = a
try:
    result = result.b
except AttributeError:
    pass
else:
    result = result.c
    try:
        result = result.d
    except AttributeError:
        pass
    else:
        result = result.e
# New
a?.b.c?.d.e

Однако PEP 505 сейчас в состоянии Deferred.

Ну и конечно следовало в статье упомянуть asyncio — это важная часть, по которой сделано много хорошей и качественно работы.
0
а ничего что по идее должно выбрасываться exception и исполнение прекращается, а тут проглатываются эксепшены и выдаётся None которое может стать ложноположительным?
Only those users with full accounts are able to leave comments. , please.