Pull to refresh

Comments 50

Еще бы использование аннотаций давало бы какой-то бонус к производительности…
О. А как по-вашему, статичческая типизация даёт или не даёт бонус?
Вы про производительность программиста или скорость работы кода?
Про работу кода. Как в Cython дописал типы переменных — получи более быстрое выполнение.
Где-то мне попадались планы разработчиков Cython по использованию новых аннотация типов для оптимизации. Будем надеяться, что они это реализуют.
UFO just landed and posted this here
Говорят, PEP по типизации располагает к созданию инструментов, которые бы использовали эти самые аннотации в целях улучшения производительности. Вот вдруг JIT нам прикрутят? :)
В больших проектах оно дает неплохой прирост производительности в командной работе и чистоте кода. Когда ты видишь, какие аргументы приходят тебе в функцию и какие надо передавать в другие, тебе меньше надо заниматься Goto Definition и чтением чужого кода. Это, во-первых, а, во-вторых, Type Hinting не располагает прокидывать через лапшу методов какой-нибудь dict, в который каждый из методов что-то подкладывает, а следующий чего-то ожидает там найти.
Есть mypyc. Компилирует код с аннотациями. Вообще, производительность программиста улучшает тоже, позволяет ловить ошибки, которые иначе всплыли бы только в рантайме и их пришлось бы искать.
Вообще конечно аннотация изрядно засирает код. Кстати не кто не знает может есть плагин к Pycharm который скрывает аннотаи?

Чтобы не писать аннотации в код, можно использовать stub-файлы — файлы с расширением *.pyi

С аннотациями сложных структур на базе словаря все не так просто, пока есть официально поддерживаемое mypy решение TypedDict (https://mypy.readthedocs.io/en/latest/more_types.html#typeddict), однако это еще не в стандарте, кроме того, там с Optional не все просто — нельзя сказать что конкретного элемента может не быть, только всей структуре указать, что допускается отсутствие ключей
total=False

Ну и сам он не то, чтобы очень удобен в эксплуатация — каждый вложенный словарь тоже должен объявляться отдельно как TypedDict

А зачем именно словари, когда есть namedtuple и dataclass?

Иногда нужно работать именно с dict, напр. десереализованный ответ в виде json и наоборот
И, кстати говоря, использование вышеупомянутых способов по-моему не решает описанную проблему :)

По моему опыту обычно отсутствие поля в json считают эквивалентным null в качестве значения.

Бывает так, что не ставят в null, а просто отсутствует ключ, и вот здесь кроется проблема. Вроде бы хочется точно сказать, вот этот key вот тут optional, а нельзя, только всю структуру тоталить (если оно на первом уровне)

Насколько видел, многие сериализаторы приучены выкидывать ключи, для которых стоит null значение.

Хочу немного дополнить: очень часто возникает ситуация (например, обработка JSON), когда надо типизировать Dict вида
from .types import ImageJson

image: ImageJson = {
    "src": "/image.jpg",
    "srcset": {
        "@2x": "/image@2x.jpg"
    }
}

Для таких ситуаций для python до 3.7 подходит TypedDict из mypy_extensions
from typing import Dict

from mypy_extensions import TypedDict

ImageJson = TypedDict('ImageJson', {
    'src': str,
    'srcset': Dict[str, str]
})

А в python 3.7 появились dataclass и лучше использовать их.

Опять же, почему не dataclass?


@dataclass
class Image:
    src: str
    srcset: Dict[str, str]

Делал, кстати, себе небольшую либу для разбора словарей в инстансы датаклассов, которая как раз юзает аннотации типов для правильной инициализации.

dataclass появились в python 3.7, это достаточно недавно, typehints появились раньше.
Поправил свой комментарий выше.

Для 3.6 датаклассы есть в виде библиотеки. typehints по сути тоже с 3.6

UFO just landed and posted this here

pip все берет из PyPi, это же не apt/yum.


Насколько я понимаю, бэкпорт для 3.6 сделал автор PEP про датаклассы.

UFO just landed and posted this here

И, кстати, в 3.6 есть NamedTuple, хотя его я никогда не юзал.


class Employee(NamedTuple):
    src: str
    srcset: Dict[str, str]
NamedTuple это все же неизменяемый, т.е. в ситуации, где надо принять JSON, что-то в нем поменять и отдать обратно, не подходит.
Mypy работает даже в 2.7 (они так говорят, я не проверял), т.е. уже в 2.7 можно использовать typehints.
В дополнение к этому, для внедрения typehints в проекты, где изначально не было ни typehints ни dataclass, проще создать тип для словаря и его указать, чем переделывать со словарей на dataclass.
Я полностью согласен с тем, что новые проекты желательно делать на 3.7 и с dataclass и прочими прелестями, но есть применение и для TypedDict.

А что значит неизменяемый?


>>> class Employee(NamedTuple):
...    src: str
...    srcset: Dict[str, str]
... 
>>> e = Employee(src="1", srcset={"2": "4"})
>>> e.srcset["2"] = "7"
>>> e.srcset["4"] = "17"
>>> e
Employee(src='1', srcset={'2': '7', '4': '17'})
>>> 
А теперь попробуйте поменять e.src.
Или записать в e.srcset прямо новый словарь, а не менять значения в старом.

То, что неизменяемый контейнер может содержать изменяемые элементы, никак не меняет того, что контейнер — неизменяемый.

Хм. Похоже, чистая динамическая типизация вымирает. PHP, Python, Ruby — все языки обрастают типами. Да и JavaScript уже заменяется на TypeScript'ы всякие. Любопытная картина.

Не вымирает, а просто больше стало свободы выбора как кодить. По-умолчанию будет всё та же динамическая утиная типизация, а когда надо или хочется — аннотация типов.
Первый вариант использую когда нужно быстро накидать скрипты (1-2 файла = 1 задача), где и так понятно что должно происходить, какие данные будут, и т.д., второй вариант — когда делается проект.

Но для того, что это работало нормально, осталось всего ничего — проаннотировать все библиотеки для питона.

Можно тогда немного странный вопрос задам? Я как бы ненастоящий сварщик, но довольно часто пишу на Python под какие-то свои задачи. Как мне корректно узнать предполагаемый тип переменной, если он чуть менее очевидный, чем int или str?

Например:
def get_certificate_san(x509cert):
    san = ''
    ext_count = x509cert.get_extension_count()
    for i in range(0, ext_count):
        ext = x509cert.get_extension(i)
        if 'subjectAltName' in str(ext.get_short_name()):
            san = ext.__str__()
    return san

Как проще всего узнать тип ext_count? Нет, я могу, конечно весь код усеять print(type(variable)) и потом выписать вручную. Но как-то выглядит некрасиво. А типы иногда бывают неочевидными, если просто смотреть на код.

UPD
Нашел в PyCharm раздел, который это описывает. Интересно) www.jetbrains.com/help/pycharm/type-hinting-in-product.html

Наверно, я немножко опоздал с ответом, но в подобной ситуации тип x509cert по идее должен быть заранее известен тому, кто пишет эту функцию. Беглый гуглинг подсказывает, что скорее всего подразумевается что-то вроде этого:


from OpenSSL.crypto import X509

def get_certificate_san(x509cert: X509) -> str:
    ext_count = x509cert.get_extension_count()
    # ...

Дальше, если в библиотеке прописаны аннотации путей или если они очевидно выводятся, PyCharm и другие статические анализаторы автоматически догадаются, что метод get_extension_count() у класса X509 возвращает int и что ext_count будет иметь соответствующий тип, без дополнительных подсказок.


К сожалению, конкретно pyOpenSSL, похоже, немного поленился прописать аннотации типов у себя (и в typeshed прописано не всё), так что работать это наверно будет плоховато (и я не в курсе, умеет ли кто-нибудь вытаскивать типы из docstring'ов)

Блин. Трудоёмко для всяких специфичных типов. Хотя нужно, согласен.

Что дает указание типов кроме, собственно, самого указания? Прирост скорости? Динамическая типизация тем и хороша, что можно не думать о типах. А так получается та же джава только в профиль.

Проверки во время разработки.

UFO just landed and posted this here
Как по мне, так указание типов — это избыточная информация. Код станет сложнее читать, если раньше ты мог быстро просмотреть код, то теперь тебе приходится больше его расшифровывать. Представьте тысячи строк кода, с указанием типов… Согласен с Вами, Java в профиль, вот только там легче будут читаться тысячи строк. Кстати, создатель Ruby отказался от данной идеи!
Код станет сложнее читать, если раньше ты мог быстро просмотреть код, то теперь тебе приходится больше его расшифровывать.

А зачем что-то расшифровывать?


Представьте тысячи строк кода, с указанием типов…

А что их представлять, я их каждый день вижу.

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

Тогда зачем нужна вообще динамическая типизация, если надо тщательно вчитываться в код и угадывать, есть статическая типизация, есть языки, в которых без нее никуда! Вам не надо ни чего угадывать.Динамическая типизация, становится псевдо типизацией и противоречит своей природе.
UFO just landed and posted this here
Называешь переменные правильно и не нужно угадывать.

Прописываешь аннотации правильно и не нужно с длинными переменными возиться ;)

И как это поможет в чтении кода глазами? Никак. Но да IDE тебе подскажет что ты не тот тип пихаешь.

Очень даже помогает, потому что можно просто сходить и почитать аннотации у функции, результат которой помещается в обсуждаемую переменную. Без аннотаций придётся читать такую функцию целиком, выясняя, какие же типы она может вернуть. В особо запущенных случаях функция возвращает результат вызова другой функции, и я так по цепочке могу на полчаса зарыться в исходники какой-то библиотеки на гитхабе, выясняя, какой же там блин тип. Особенно если к функции написана хреновая документация (таким грешат даже «взрослые» проекты вроде Django)

Соглашусь с обоими спикерами. Нормальные имена датут большую часть информации о том, что лежит в переменной. Например, users — скорее всего iterable, содержащий User. А тайпинг даст уточнения: например, не Iterable, а Sequence или волбще List, или например, что допустим None (см. Optional).

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

from typing import Dict

book_authors: Dict[str, str] = {”Fahrenheit 451”: “Bradbury”}
book_authors[“1984”] = 0 # Incompatible types in assignment (expression has type “int”, target has type “str”)
book_authors[1984] = “Orwell” # Invalid index type “int” for “Dict[str, str]”; expected type “str”
print(book_authors)

# Выводит {'Fahrenheit 451': 'Bradbury', '1984': 0, 1984: 'Orwell'} без каких‑либо ошибок и предупреждений.

Питон НЕ проверяет тайпхинты в процессе выполнения. Надо использовать ДО запуска отдельные инструменты типа mypy/pyright.

Sign up to leave a comment.