Differences between revisions 2 and 4 (spanning 2 versions)
Revision 2 as of 2020-11-30 20:07:35
Size: 7658
Editor: FrBrGeorge
Comment:
Revision 4 as of 2020-12-01 09:57:15
Size: 9379
Editor: FrBrGeorge
Comment:
Deletions are marked like this. Additions are marked like this.
Line 10: Line 10:
Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое:  Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое:
Line 30: Line 30:
C = class('Simple', (), {'val': 42, 'getval': lambda self: self.val}) C = type('Simple', (), {'val': 42, 'getval': lambda self: self.val})
Line 50: Line 50:
}}}     }}}
Line 86: Line 86:
}}} }}}
 * Модуль [[py3doc:types]]
Line 125: Line 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
Line 126: Line 133:
 * Хо-хо! Смотрите, какая разница между 3.8 и 3.9!  * Алиасы (практически `typedef`), `Any`, `NewType` (категоризация), `Callable`
 * Дженерики и [[py3doc:collections.abc]]
 * Инструменты: `NoReturn`, `Union`, `Optional`, `Type` (если сама переменная — класс), `Literal`, `Final`, …
Line 128: Line 137:
'''TODO'''
 * [[https://habr.com/ru/company/lamoda/blog/432656/|Развесистая статья на Хабре]]
Отложенная аннотация: [[pep:pep-0563]]

 * [[https://habr.com/ru/company/lamoda/blog/432656/|Развесистая статья на Хабре]] (⩽ Python3.8, однако ☺, см [[pep:pep-0585]])
Line 135: Line 145:
Зачем аннотации?
 * Дисциплина программирования
  * большие, сверхбольшие и «долгие» проекты
 * ''Потенциально'' возможные проверки (как в [[py3doc:dataclass]])
 * Прагматика, включенная в синтаксис языка
Line 136: Line 151:
'''TODO''' [[http://www.mypy-lang.org]]: ''статическая'' типизация в Python (ну, почти… или ''совсем''!)
 * --(Описание типов переменных, параметров и т. п.)---
 * Проверка выражений с типизированными данными
  * В т. .ч ''не''-проверка нетипизиварованных
 * ''Компиляция''?
  * Bleedng edge: [[https://github.com/mypyc/mypyc|было]], [[https://github.com/python/mypy/tree/master/mypyc|стало]]
Line 157: Line 177:

== Д/З ==
'''TODO'''

Метаклассы и аннотации

Это две совсем разные темы, если что).

Метаклассы

Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое:

Итак.

  • Класс можно создать просто функцией (aka Monkey patch)

  • Декоратором
  • От класса можно унаследоваться и всё модифицировать в потомке
    • TODO but why then?

  • Создание класса с помощью type(name, bases, dict)

       1 class C:
       2     pass
    
    • это вырожденный вызов type("имя", (кортеж родителей), {пространство имён})

       1 C = type("C", (), {})
    
  • Например,
       1 C = type('Simple', (), {'val': 42, 'getval': lambda self: self.val})
       2 c = C()
       3 c.val, c.getval()
    
  • Но type — это просто класс такой ⇒ от него можно унаследоваться, например, перебить ему __init__():

       1 class overtype(type):
       2     def __init__(self, Name, Parents, Dict):
       3         print(f" Class definition: {Name}{Parents}: {Dict}")
       4         super().__init__(Name, Parents, Dict)
       5 
       6 Boo = overtype("Boo", (), {"A": 100500})
       7 t = Boo()
       8 print(Boo, t, t.A)
    
  • а вот это Boo = overtype… можно записать так:

       1 
       2 class Boo(metaclass=overtype):
       3     A = 100500
    
  • (по сути, 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 not cls._instance:
       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

Модуль typing

  • Алиасы (практически typedef), Any, NewType (категоризация), Callable

  • Дженерики и collections.abc

  • Инструменты: NoReturn, Union, Optional, Type (если сама переменная — класс), Literal, Final, …

Отложенная аннотация: pep-0563

MyPy

Зачем аннотации?

  • Дисциплина программирования
    • большие, сверхбольшие и «долгие» проекты
  • Потенциально возможные проверки (как в dataclass)

  • Прагматика, включенная в синтаксис языка

http://www.mypy-lang.org: статическая типизация в Python (ну, почти… или совсем!)

  • Описание типов переменных, параметров и т. п.-

  • Проверка выражений с типизированными данными
    • В т. .ч не-проверка нетипизиварованных

  • Компиляция?

Пример для mypyc

   1 import time
   2 from typing import Tuple
   3 
   4 def fb(x:int,y:int)->Tuple[int,int]:
   5     return y,x+y
   6 
   7 def test()->float:
   8     x:int=0
   9     y:int=1
  10     t:float=time.time()
  11     for i in range(1000000):
  12         x = 0
  13         y = 1
  14         for j in range(100):
  15             x,y=fb(x,y)
  16     return time.time()-t

Д/З

TODO

LecturesCMC/PythonIntro2020/13_MetaclassAnnotations (last edited 2020-12-06 15:50:33 by FrBrGeorge)