7658
Комментарий:
|
12459
|
Удаления помечены так. | Добавления помечены так. |
Строка 10: | Строка 10: |
Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое: | Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое: |
Строка 30: | Строка 30: |
C = class('Simple', (), {'val': 42, 'getval': lambda self: self.val}) | C = type('Simple', (), {'val': 42, 'getval': lambda self: self.val}) |
Строка 50: | Строка 50: |
}}} | }}} |
Строка 76: | Строка 76: |
if not cls._instance: | if cls._instance is None: |
Строка 86: | Строка 86: |
}}} | }}} * Модуль [[py3doc:types]] |
Строка 125: | Строка 126: |
Python 3.9 с нами :):: Просто прочитаем [[https://docs.python.org/3/whatsnew/3.9.html|What’s New In Python 3.9]] * [[https://docs.python.org/3/whatsnew/3.9.html#type-hinting-generics-in-standard-collections|в частности, дженерики]] ([[pep:pep-0585]]) * Для указания, какого типа, например, ''элементы'' списка * `list[int]` в 3.9 vs. 3.8 |
|
Строка 126: | Строка 133: |
* Хо-хо! Смотрите, какая разница между 3.8 и 3.9! '''TODO''' * [[https://habr.com/ru/company/lamoda/blog/432656/|Развесистая статья на Хабре]] |
* Алиасы (практически `typedef`), `Any`, `NewType` (категоризация), `Callable` * Дженерики и [[py3doc:collections.abc]] * Инструменты: `NoReturn`, `Union`, `Optional`, `Type` (если сама переменная — класс), `Literal`, `Final`, … Отложенная аннотация: [[pep:pep-0563]] * [[https://habr.com/ru/company/lamoda/blog/432656/|Развесистая статья на Хабре]] (⩽ Python3.8, однако ☺, см [[pep:pep-0585]]) |
Строка 134: | Строка 144: |
'''Важно''': в Python есть ''поддержка'' аннотаций, но практически нет их ''использования'' (разве что в [[py3doc:dataclasses]]). В язык не входит, делайте сами. | |
Строка 135: | Строка 146: |
'''TODO''' |
Зачем аннотации? * Дисциплина программирования * большие, сверхбольшие и «долгие» проекты * ''Потенциально'' возможные проверки * Прагматика, включенная в синтаксис языка * Преобразование Python-кода в представления, требующие статической типизации * … [[http://www.mypy-lang.org]]: ''статическая'' типизация в Python (ну, почти… или ''совсем''!) * --(Описание типов переменных, параметров и т. п.)--- * Проверка выражений с типизированными данными * В т. .ч ''не''-проверка нетипизиварованных * Пример: {{{#!python def fun(a: int, b) -> str: b *= a return b def fun2(a: int) -> str: c: int = a + "1" return c res: str var: int res = fun(1,"qwe") res = fun(100, 200) var = fun(1,2) }}} * Он запускается! Но проверку на статическую типизацию не проходит: {{{#!highlight console $ mypy ex1.py ex1.py:6: error: Unsupported operand types for + ("int" and "str") ex1.py:7: error: Incompatible return value type (got "int", expected "str") ex1.py:13: error: Incompatible types in assignment (expression has type "str", variable has type "int") Found 3 errors in 1 file (checked 1 source file) $ mypy --strict ex1.py ex1.py:1: error: Function is missing a type annotation for one or more arguments ex1.py:3: error: Returning Any from function declared to return "str" ex1.py:6: error: Unsupported operand types for + ("int" and "str") ex1.py:7: error: Incompatible return value type (got "int", expected "str") ex1.py:13: error: Incompatible types in assignment (expression has type "str", variable has type "int") Found 5 errors in 1 file (checked 1 source file) }}} * ''Компиляция''. Если все объекты полностью типизированы, у них имеется эквивалент в виде соответствующих структур [[https://docs.python.org/3/c-api/index.html|PythonAPI]]. ЧСХ, у байт-кода __тоже__ есть эквивалент в Python API * Таинственный `mypyc` * Bleedng edge: [[https://github.com/mypyc/mypyc|было]], [[https://github.com/python/mypy/tree/master/mypyc|стало]] * Пока не рекомендуют использовать, но сами все свои модули им компилируют! |
Строка 157: | Строка 212: |
* Сравнение производительности: {{{#!console $ mypyc speed.py running build_ext building 'speed' extension creating build/temp.linux-x86_64-3.8 creating build/temp.linux-x86_64-3.8/build x86_64-alt-linux-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -pipe -frecord-gcc-switches -Wall -g -O3 -fPIC -I/home/george/.local/lib/python3/site-packages/mypyc/lib-rt -I/usr/include/python3.8 -c build/__native.c -o build/temp.linux-x86_64-3.8/build/__native.o -O3 -Werror -Wno-unused-function -Wno-unused-label -Wno-unreachable-code -Wno-unused-variable -Wno-unused-command-line-argument -Wno-unknown-warning-option -Wno-unused-but-set-variable x86_64-alt-linux-gcc -pthread -shared build/temp.linux-x86_64-3.8/build/__native.o -L/usr/lib64 -o /home/george/src/mypyex/speed.cpython-38.so $ mv speed.py speed_.py $ python3 -c "import speed_; print(speed_.test())" 12.719617366790771 $ python3 -c "import speed; print(speed.test())" 2.643144130706787 }}} == Д/З == '''TODO''' |
Метаклассы и аннотации
Это две совсем разные темы, если что).
Метаклассы
Внезапно развёрнутое описание на StackOverflow (перевод на Хабре)
Забойная статья Sebastian Buczyński 2020 года (Перевод)
Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое:
enum (в частности, How are Enums different?)
Итак.
Класс можно создать просто функцией (aka Monkey patch)
- Декоратором
- От класса можно унаследоваться и всё модифицировать в потомке
TODO but why then?
Создание класса с помощью type(name, bases, dict)
это вырожденный вызов type("имя", (кортеж родителей), {пространство имён})
1 C = type("C", (), {})
- Например,
Но type — это просто класс такой ⇒ от него можно унаследоваться, например, перебить ему __init__():
а вот это Boo = overtype… можно записать так:
(по сути, class C: — это class C(metaclass=type):)
__call__() → (__prepare__() для автоматического создания пространства имён, если есть), __new__(), __init__()
__new__()
создаёт экземпляр объекта (а __init__() заполняет готовый)
это метод класса (такой @classmethod без декоратора)
в нём можно поменять всё, что в __init__() приезжает готовое и read-only: __slots__, имя класса (если это метакласс) и т. п.
Два примера:
- Ненаследуемый класс
1 class final(type): 2 def __new__(metacls, name, parents, namespace): 3 for cls in parents: 4 if isinstance(cls, final): 5 raise TypeError(f"{cls.__name__} is final") 6 return super(final, metacls).__new__(metacls, name, parents, namespace) 7 class E(metaclass=final): pass 8 class C: pass 9 class A(C, E): pass
Синглтон (больше синглтонов тут)
1 class Singleton(type): 2 _instance = None 3 def __call__(cls, *args, **kw): 4 if cls._instance is None: 5 cls._instance = super(Singleton, cls).__call__(*args, **kw) 6 return cls._instance 7 8 class S(metaclass=Singleton): 9 A = 3 10 s, t = S(), S() 11 s.newfield = 100500 12 print(f"{s.newfield=}, {t.newfield=}") 13 print(f"{s is t=}")
Модуль types
Аннотации
Duck typing:
- Экономия кода на описаниях и объявлениях типа
- Экономия (несравненно бо́льшая) кода на всех этих ваших полиморфизмах
- ⇒ Компактный читаемый код, хорошее отношение семантика/синтаксис
- ⇒ Быстрое решение Д/З ☺
Однако:
- Практически все ошибки — runtime
- Много страданий от невнимательности (передал объект не того типа, и не заметил, пока не свалилось)
Вашей невнимательности не поможет даже хитрое IDE: оно тоже не знает о том, какого типа объекты правильные, какого — нет
- (соответственно, о полях вашего объекта тоже)
Часть прагматики растворяется в коде (например, вы написали строковую функцию, как об этом узнать?)
- Большие и сильно разрозненные проекты — ?
Поэтому нужны указания о типе полей классов, параметрах и возвращаемых значений функций/методов и т. п. — Аннотации
- Пример аннотаций полей, параметров и возвращаемых значений
1 class C: 2 A: int = 2 3 def __init__(self, param: int = None, signed: bool = True): 4 if param != None: 5 self.A = param if signed else abs(param) 6 7 def mult(self, mlt) -> str: 8 return self.A * mlt 9 10 a, b = C(3), C("QWE") 11 print(f"{a.mult([2])=}, {b.mult(2)=}") 12 print(f"{a.__annotations__=}") 13 print(f"{a.mult.__annotations__=}") 14 print(f"{C.__init__.__annotations__}")
- Аннотации сами по себе не влияют на семантику функции
Типы в аннотациях — это настоящие типы
Про аннотацию переменных на DZone (перевод на tproger)
Составные и нечёткие типы
- Python 3.9 с нами :)
Просто прочитаем What’s New In Python 3.9
в частности, дженерики (pep-0585)
Для указания, какого типа, например, элементы списка
list[int] в 3.9 vs. 3.8
Модуль typing
Алиасы (практически typedef), Any, NewType (категоризация), Callable
Дженерики и collections.abc
Инструменты: NoReturn, Union, Optional, Type (если сама переменная — класс), Literal, Final, …
Отложенная аннотация: pep-0563
Развесистая статья на Хабре (⩽ Python3.8, однако ☺, см pep-0585)
dataclass — типизированные структуры
Важно: в Python есть поддержка аннотаций, но практически нет их использования (разве что в dataclasses). В язык не входит, делайте сами.
MyPy
Зачем аннотации?
- Дисциплина программирования
- большие, сверхбольшие и «долгие» проекты
Потенциально возможные проверки
- Прагматика, включенная в синтаксис языка
- Преобразование Python-кода в представления, требующие статической типизации
- …
http://www.mypy-lang.org: статическая типизация в Python (ну, почти… или совсем!)
Описание типов переменных, параметров и т. п.-
- Проверка выражений с типизированными данными
В т. .ч не-проверка нетипизиварованных
- Пример:
- Он запускается! Но проверку на статическую типизацию не проходит:
1 $ mypy ex1.py 2 ex1.py:6: error: Unsupported operand types for + ("int" and "str") 3 ex1.py:7: error: Incompatible return value type (got "int", expected "str") 4 ex1.py:13: error: Incompatible types in assignment (expression has type "str", variable has type "int") 5 Found 3 errors in 1 file (checked 1 source file) 6 $ mypy --strict ex1.py 7 ex1.py:1: error: Function is missing a type annotation for one or more arguments 8 ex1.py:3: error: Returning Any from function declared to return "str" 9 ex1.py:6: error: Unsupported operand types for + ("int" and "str") 10 ex1.py:7: error: Incompatible return value type (got "int", expected "str") 11 ex1.py:13: error: Incompatible types in assignment (expression has type "str", variable has type "int") 12 Found 5 errors in 1 file (checked 1 source file) 13
Компиляция. Если все объекты полностью типизированы, у них имеется эквивалент в виде соответствующих структур PythonAPI. ЧСХ, у байт-кода тоже есть эквивалент в Python API
Таинственный mypyc
- Пока не рекомендуют использовать, но сами все свои модули им компилируют!
Пример для mypyc
- Сравнение производительности:
$ mypyc speed.py running build_ext building 'speed' extension creating build/temp.linux-x86_64-3.8 creating build/temp.linux-x86_64-3.8/build x86_64-alt-linux-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -pipe -frecord-gcc-switches -Wall -g -O3 -fPIC -I/home/george/.local/lib/python3/site-packages/mypyc/lib-rt -I/usr/include/python3.8 -c build/__native.c -o build/temp.linux-x86_64-3.8/build/__native.o -O3 -Werror -Wno-unused-function -Wno-unused-label -Wno-unreachable-code -Wno-unused-variable -Wno-unused-command-line-argument -Wno-unknown-warning-option -Wno-unused-but-set-variable x86_64-alt-linux-gcc -pthread -shared build/temp.linux-x86_64-3.8/build/__native.o -L/usr/lib64 -o /home/george/src/mypyex/speed.cpython-38.so $ mv speed.py speed_.py $ python3 -c "import speed_; print(speed_.test())" 12.719617366790771 $ python3 -c "import speed; print(speed.test())" 2.643144130706787
Д/З
TODO