Различия между версиями 1 и 2
Версия 1 от 2020-11-30 21:04:00
Размер: 2317
Редактор: FrBrGeorge
Комментарий:
Версия 2 от 2020-11-30 23:07:35
Размер: 7658
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 5: Строка 5:
 * В [[py3doc:datamodel#metaclasses|справочнике]]  * В [[py3ref:datamodel#metaclasses|справочнике]]
 * Внезапно развёрнутое описание [[https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python?rq=1|на StackOverflow]] ([[https://habr.com/ru/post/145835/|перевод на Хабре]])
 * [[https://sahandsaba.com/python-classes-metaclasses.html|Статья Sahand Saba]] ([[https://ru.hexlet.io/blog/posts/prodvinutyy-python-chast-3-klassy-i-metaklassy|перевод]])
 * Забойная статья [[https://breadcrumbscollector.tech/when-to-use-metaclasses-in-python-5-interesting-use-cases/|Sebastian Buczyński]] 2020 года ([[https://webdevblog.ru/kogda-ispolzovat-metaklassy-v-python-5-interesnyh-variantov-ispolzovaniya/|Перевод]])
Строка 7: Строка 10:
Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое: [[py3doc:enum]] (в частности, [[py3doc:enum.html#how-are-enums-different|How are Enums different?]])
 * Класс можно создать просто функцией (а можно и декоратором)
Хороший пример real-life кода на Python, эксплуатирующий метаклассы и многое другое:
 *
[[py3doc:enum]] (в частности, [[py3doc:enum.html#how-are-enums-different|How are Enums different?]])
 * [[py3doc:abc]]

Итак.
 *
Класс можно создать просто функцией (aka [[WP:Monkey patch]])
 * Декоратором
Строка 44: Строка 52:
[[py3doc:datamodel#metaclasses|Подробности]]: [[py3ref:datamodel#metaclasses|Подробности]]:
Строка 46: Строка 54:
 *
 * определённые правила для [[py3ref:datamodel.html#object.__init__|__init__()]] и [[py3ref:datamodel.html#object.__new__|__new__()]]
 * `__new__()`
  * ''создаёт'' экземпляр объекта (а `__init__()` заполняет готовый)
  * это метод класса (такой `@classmethod` без декоратора)
  * в нём можно поменять всё, что в `__init__()` приезжает готовое и read-only: `__slots__`, имя класса (если это метакласс) и т. п.
Два примера:
 * Ненаследуемый класс
 {{{#!python
class final(type):
    def __new__(metacls, name, parents, namespace):
        for cls in parents:
            if isinstance(cls, final):
                raise TypeError(f"{cls.__name__} is final")
        return super(final, metacls).__new__(metacls, name, parents, namespace)
class E(metaclass=final): pass
class C: pass
class A(C, E): pass
}}}
 * Синглтон (больше синглтонов [[https://webdevblog.ru/realizaciya-shablona-singleton-v-python/|тут]])
 {{{#!python
class Singleton(type):
    _instance = None
    def __call__(cls, *args, **kw):
        if not cls._instance:
             cls._instance = super(Singleton, cls).__call__(*args, **kw)
        return cls._instance
Строка 49: Строка 80:
<!> Не путать с наследованием! class S(metaclass=Singleton):
    A = 3
s, t = S(), S()
s.newfield = 100500
print(f"{s.newfield=}, {t.newfield=}")
print(f"{s is t=}")
}}}

== Аннотации ==
Duck typing:
 * Экономия кода на описаниях и объявлениях типа
 * Экономия (несравненно бо́льшая) кода на всех этих ваших полиморфизмах
 * ⇒ Компактный читаемый код, хорошее отношение семантика/синтаксис
 * ⇒ Быстрое решение Д/З ☺
Однако:
 * Практически все ошибки — runtime
 * Много страданий от невнимательности (передал объект не того типа, и не заметил, пока не свалилось)
  * Вашей невнимательности не поможет даже хитрое IDE: оно ''тоже'' не знает о том, какого типа объекты правильные, какого — нет
  * (соответственно, о полях вашего объекта тоже)
 * Часть ''прагматики'' растворяется в коде (например, вы написали ''строковую'' функцию, как об этом узнать?)
 * Большие и сильно разрозненные проекты — ?

Поэтому нужны указания о типе полей классов, параметрах и возвращаемых значений функций/методов и т. п. — [[py3tut:controlflow#function-annotations|Аннотации]]
 * Пример аннотаций полей, параметров и возвращаемых значений
 {{{#!python
class C:
    A: int = 2
    def __init__(self, param: int = None, signed: bool = True):
        if param != None:
            self.A = param if signed else abs(param)

    def mult(self, mlt) -> str:
        return self.A * mlt

a, b = C(3), C("QWE")
print(f"{a.mult([2])=}, {b.mult(2)=}")
print(f"{a.__annotations__=}")
print(f"{a.mult.__annotations__=}")
print(f"{C.__init__.__annotations__}")
}}}
 * Аннотации сами по себе не влияют на семантику функции
 * Типы в аннотациях — это ''настоящие'' типы
 * Про аннотацию переменных [[https://dzone.com/articles/new-in-python-syntax-for-variable-annotations|на DZone]] ([[https://tproger.ru/translations/python-variable-annotations/|перевод на tproger]])

=== Составные и нечёткие типы ===
Модуль [[py3doc:typing]]
 * Хо-хо! Смотрите, какая разница между 3.8 и 3.9!

'''TODO'''
 * [[https://habr.com/ru/company/lamoda/blog/432656/|Развесистая статья на Хабре]]
  * [[https://habr.com/ru/company/lamoda/blog/435988/|Её продолжение]]
 * [[py3doc:dataclass]] — типизированные структуры
  * [[https://habr.com/ru/post/415829/|тот же автор на Хабре]]

=== MyPy ===

'''TODO'''

Пример для `mypyc`
{{{#!python
import time
from typing import Tuple

def fb(x:int,y:int)->Tuple[int,int]:
    return y,x+y

def test()->float:
    x:int=0
    y:int=1
    t:float=time.time()
    for i in range(1000000):
        x = 0
        y = 1
        for j in range(100):
            x,y=fb(x,y)
    return time.time()-t
}}}

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

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

Метаклассы

Хороший пример 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 = class('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=}")
    

Аннотации

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)

Составные и нечёткие типы

Модуль typing

  • Хо-хо! Смотрите, какая разница между 3.8 и 3.9!

TODO

MyPy

TODO

Пример для 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

LecturesCMC/PythonIntro2020/13_MetaclassAnnotations (последним исправлял пользователь FrBrGeorge 2020-12-06 18:50:33)