‣ 12+ лет опыта, 7 лет с Python, 6 с JS ‣ Работал в oDesk, Helios, 42cc. ‣ Соорганизатор PyCon Ukraine, KyivJS ‣ CTO в CartFresh Обо мне
‣ Стартап по доставке продуктов на дом ‣ Работаем как CartFresh (Boston, US) и ZAKAZ.UA (Киев, Днепр, Одесса, Харьков) ‣ Apache CouchDB, Apache Solr, Redis ‣ python back-end CartFresh
Эта презентация не о ‣ автогенерации кода ‣ поиске серебряной пули ‣ проверке типов в рантайме ‣ статически типизированным питоне
‣ Немного зачем это надо ‣ Как это выглядит ‣ Тулы и как можно пользоваться на практике Содержание
Теория типов
Пусть в некой деревне живёт брадобрей, который бреет тех и только тех, которые не бреются сами. Бреет ли брадобрей сам себя? Парадокс Рассела
‣ В 1908 Russell & Whitehead доставили “"ramified" theory of types” ‣ В 1920-х Leon Chwistek and Frank P. Ramsey доставили "simple type theory” ‣ 1940 Alonzo Church "A Formulation of the Simple Theory of Types” ‣ 1972 Girard–Reynolds discovered System F (polymorphic lambda calculus) История
‣ Пацаны обратили внимание, что у простой теории типов есть интересные свойства и может быть просто расширена до поддержки Декартового произведения и дизъюнктного объединения ‣ Поэтому простую теорию типов используют в дизайне языков программирования ‣ А System F помогла улучшила простую теорию типов и такое полезное свойство, как полиморфизм Теория
‣ Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention PEP 484 def f(x: str) -> str:
Gradual Typing ‣ Разработана в 2006 году Jeremy Siek и Walid Taha ‣ Идея состоит в том, чтобы позволить части кода быть динамически типизированным, а части –  статически
Нафига ‣ Статическое описание типа аргументов и возвращаемого значения функций упрощает чтение кода и помогает формировать документацию ‣ Поможет обнаружить баги за меньшее количество времени ‣ Поможет найти сложновоспроизводимые баги быстрее
Напоминаю, чтение кода ‣ IBM 1989: 50% of the effort in accomplishing a task for the programmer is towards understanding the system ‣ Bell Labs 1992: 60%-80% of their time understanding code, 20% as the developers gain experience with current codebase ‣ National Research Council 1997 (Canada): over 25% of their time either searching for or looking at code ‣ Peter Hallam 2006: 70% during personal experiment ‣ Microsoft 2007: 65% (survey)
Что было ‣ PyCharm умел делать статический анализ контекста использования переменных. Мог трекать до 60% случаев. Похоже на Facebook’s flow для JavaScript ‣ Prospector – смесь статического анализатора с PyLint ‣ In-house tools по анализу docstring etc.
Python
Определение в контексте Python: Предполагается, что тип – это множество значений и множество функций, к которым можно применить эти значения.
Gradual Typing ‣ Тип t1 консистентен с типом t2 если t1 это подтип t2 (но не наоборот!) ‣ Any консистентен со всеми типами (но не является подтипом) ‣ Все типы являются подтипом Any
Отношение между подтипами ‣ Каждый тип является подтипом самого себя ‣ Множество значений уменьшается с увеличением количества функций, которые работают с этим подтипом
Типы vs Классы Классы являются фабриками объектов, определенных с помощью class . Класс – динамическая/runtime концепция
Аннотации ‣ Аннотации типов являются опциональными ‣ Никак не влияют на выполнение ‣ lambda-функции не поддерживают аннотации
Синтаксис def f(x: int) -> int: return x + 1 print(f(1)) def f1(x: int) -> int: y = "1" # type: int return x + y print (f(2))
Python 2 def embezzle(account, funds=1000000, *fake_receipts): # type: (str, int, *str) -> None """Embezzle funds from account using fake receipts.""" return None print embezzle(1, 1000) # hints11.py, line 9: Argument 1 to "embezzle" has incompatible type "int"; expected "str"
Граф анализа from typing import List, Any def append_pi(lst: List[float]) -> None: lst += [3.14] append_seven(lst) def append_seven(lst): lst.append("7") my_list = [1, 3, 5] # type: List[float] append_pi(my_list) print (my_list) # [1, 3, 5, 3.14, '7']
Numeric Tower Number :> Complex :> Real :> Rational :> Integral
Граф анализа from typing import List, Any def append_pi(lst: List[float]) -> None: lst += [3.14] def append_seven(lst): lst.append("7") append_pi(lst) my_list = [1, 3, 5] # type: List[float] append_seven(my_list) print (my_list) # [1, 3, 5, '7', 3.14]
Какие бывают ‣ Встроенные классы (int, bool etc.) ‣ Абстрактные классы ‣ Дополнительно из модуля typing: None , Any , Union , Tuple , Callable, Sequence, Dict ‣ Динамически вычисленные типы не работают
Алиасы from typing import TypeVar, Iterable, Tuple Url = str def fetch(url: Url): pass T = TypeVar('T', int, float, complex) def inproduct(v: Iterable[Tuple[T, T]]) -> T: return sum(x*y for x, y in v) print(inproduct(((1, 1.3),))) # 1.3
Дженерики ‣ TypeVar – фабрика для создания параметризированных дженериков, напр. AnyStr = TypeVar('AnyStr', str, bytes) ‣ def concat(x: AnyStr, y: AnyStr) -> AnyStr –  ограничения на тип сразу на оба аргумента
Дженерики from typing import TypeVar AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: return x + y print(concat("_(*~", "*)_/")) # _(*~*)_/ concat(b"_(*~", "*)_/") # TypeError: string argument without an encoding # hints4.py, line 17: Type argument 1 of "concat" has incompatible value "object"
Отношение между подтипами from typing import List class User(int): pass my_list = [1, 3, 5] # type: List[int] my_list.append(User()) print(my_list) # [1, 3, 5, 0] my_list2 = [1] # type: List[User] print (my_list2) # hints2.py, line 14: List item 1 has incompatible type "int"
Forward references class Tree: def __init__(self, left: Tree, right: Tree) -> None: self.left = left self.right = right Это не будет работать
Forward references class Tree: def __init__(self, left: ‘Tree’, right: ‘Tree’) -> None: self.left = left self.right = right Это не будет работать
Union from typing import Union, Sequence class Employee(str): pass def employees(e: Union[Employee, Sequence[Employee]]) -> None: if isinstance(e, Employee): e = [e] for _ in e: print("Employee: {}".format(_)) employees(Employee("Max K")) # Employee: Max K employees([Employee("Max K"), Employee("Dima Y")]) # Employee: Max K # Employee: Dima Y
Any Все типы являются подтипом Any
Проверка в Runtime import typing if typing.TYPE_CHECKING: import expensive_mod def a_func(arg: 'expensive_mod.SomeClass') -> None: a_var = arg # type: expensive_mod.SomeClass
Переменные x = [] # type: List[Employee] x, y, z = [], [], [] # type: List[int], List[int], List[str] x, y, z = [], [], [] # type: (List[int], List[int], List[str]) x = [ 1, 2, ] # type: List[int]
Stub-files ‣ Расширения ‣ Third-party модули без типов ‣ Standard library модули ‣ Модули, которые должны поддерживать Python 2 и 3 ‣ Модули, которые используют аннотации в другом контексте
Игнор ‣ Декоратор @no_type_check для класса или функции ‣ # type: ignore
bright future ahead
Thanks. @maxmaxmaxmax

WebCamp 2016: Python.Максим Климишин.Типизированный Python

  • 2.
    ‣ 12+ летопыта, 7 лет с Python, 6 с JS ‣ Работал в oDesk, Helios, 42cc. ‣ Соорганизатор PyCon Ukraine, KyivJS ‣ CTO в CartFresh Обо мне
  • 3.
    ‣ Стартап подоставке продуктов на дом ‣ Работаем как CartFresh (Boston, US) и ZAKAZ.UA (Киев, Днепр, Одесса, Харьков) ‣ Apache CouchDB, Apache Solr, Redis ‣ python back-end CartFresh
  • 4.
    Эта презентация нео ‣ автогенерации кода ‣ поиске серебряной пули ‣ проверке типов в рантайме ‣ статически типизированным питоне
  • 6.
    ‣ Немного зачемэто надо ‣ Как это выглядит ‣ Тулы и как можно пользоваться на практике Содержание
  • 7.
  • 8.
    Пусть в некойдеревне живёт брадобрей, который бреет тех и только тех, которые не бреются сами. Бреет ли брадобрей сам себя? Парадокс Рассела
  • 9.
    ‣ В 1908Russell & Whitehead доставили “"ramified" theory of types” ‣ В 1920-х Leon Chwistek and Frank P. Ramsey доставили "simple type theory” ‣ 1940 Alonzo Church "A Formulation of the Simple Theory of Types” ‣ 1972 Girard–Reynolds discovered System F (polymorphic lambda calculus) История
  • 10.
    ‣ Пацаны обратиливнимание, что у простой теории типов есть интересные свойства и может быть просто расширена до поддержки Декартового произведения и дизъюнктного объединения ‣ Поэтому простую теорию типов используют в дизайне языков программирования ‣ А System F помогла улучшила простую теорию типов и такое полезное свойство, как полиморфизм Теория
  • 12.
    ‣ Python willremain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention PEP 484 def f(x: str) -> str:
  • 13.
    Gradual Typing ‣ Разработанав 2006 году Jeremy Siek и Walid Taha ‣ Идея состоит в том, чтобы позволить части кода быть динамически типизированным, а части –  статически
  • 14.
    Нафига ‣ Статическое описаниетипа аргументов и возвращаемого значения функций упрощает чтение кода и помогает формировать документацию ‣ Поможет обнаружить баги за меньшее количество времени ‣ Поможет найти сложновоспроизводимые баги быстрее
  • 15.
    Напоминаю, чтение кода ‣IBM 1989: 50% of the effort in accomplishing a task for the programmer is towards understanding the system ‣ Bell Labs 1992: 60%-80% of their time understanding code, 20% as the developers gain experience with current codebase ‣ National Research Council 1997 (Canada): over 25% of their time either searching for or looking at code ‣ Peter Hallam 2006: 70% during personal experiment ‣ Microsoft 2007: 65% (survey)
  • 16.
    Что было ‣ PyCharmумел делать статический анализ контекста использования переменных. Мог трекать до 60% случаев. Похоже на Facebook’s flow для JavaScript ‣ Prospector – смесь статического анализатора с PyLint ‣ In-house tools по анализу docstring etc.
  • 17.
  • 18.
    Определение в контекстеPython: Предполагается, что тип – это множество значений и множество функций, к которым можно применить эти значения.
  • 19.
    Gradual Typing ‣ Типt1 консистентен с типом t2 если t1 это подтип t2 (но не наоборот!) ‣ Any консистентен со всеми типами (но не является подтипом) ‣ Все типы являются подтипом Any
  • 20.
    Отношение между подтипами ‣Каждый тип является подтипом самого себя ‣ Множество значений уменьшается с увеличением количества функций, которые работают с этим подтипом
  • 21.
    Типы vs Классы Классыявляются фабриками объектов, определенных с помощью class . Класс – динамическая/runtime концепция
  • 22.
    Аннотации ‣ Аннотации типовявляются опциональными ‣ Никак не влияют на выполнение ‣ lambda-функции не поддерживают аннотации
  • 23.
    Синтаксис def f(x: int)-> int: return x + 1 print(f(1)) def f1(x: int) -> int: y = "1" # type: int return x + y print (f(2))
  • 24.
    Python 2 def embezzle(account,funds=1000000, *fake_receipts): # type: (str, int, *str) -> None """Embezzle funds from account using fake receipts.""" return None print embezzle(1, 1000) # hints11.py, line 9: Argument 1 to "embezzle" has incompatible type "int"; expected "str"
  • 25.
    Граф анализа from typingimport List, Any def append_pi(lst: List[float]) -> None: lst += [3.14] append_seven(lst) def append_seven(lst): lst.append("7") my_list = [1, 3, 5] # type: List[float] append_pi(my_list) print (my_list) # [1, 3, 5, 3.14, '7']
  • 26.
    Numeric Tower Number :>Complex :> Real :> Rational :> Integral
  • 27.
    Граф анализа from typingimport List, Any def append_pi(lst: List[float]) -> None: lst += [3.14] def append_seven(lst): lst.append("7") append_pi(lst) my_list = [1, 3, 5] # type: List[float] append_seven(my_list) print (my_list) # [1, 3, 5, '7', 3.14]
  • 28.
    Какие бывают ‣ Встроенныеклассы (int, bool etc.) ‣ Абстрактные классы ‣ Дополнительно из модуля typing: None , Any , Union , Tuple , Callable, Sequence, Dict ‣ Динамически вычисленные типы не работают
  • 29.
    Алиасы from typing importTypeVar, Iterable, Tuple Url = str def fetch(url: Url): pass T = TypeVar('T', int, float, complex) def inproduct(v: Iterable[Tuple[T, T]]) -> T: return sum(x*y for x, y in v) print(inproduct(((1, 1.3),))) # 1.3
  • 30.
    Дженерики ‣ TypeVar – фабрикадля создания параметризированных дженериков, напр. AnyStr = TypeVar('AnyStr', str, bytes) ‣ def concat(x: AnyStr, y: AnyStr) -> AnyStr –  ограничения на тип сразу на оба аргумента
  • 31.
    Дженерики from typing importTypeVar AnyStr = TypeVar('AnyStr', str, bytes) def concat(x: AnyStr, y: AnyStr) -> AnyStr: return x + y print(concat("_(*~", "*)_/")) # _(*~*)_/ concat(b"_(*~", "*)_/") # TypeError: string argument without an encoding # hints4.py, line 17: Type argument 1 of "concat" has incompatible value "object"
  • 32.
    Отношение между подтипами fromtyping import List class User(int): pass my_list = [1, 3, 5] # type: List[int] my_list.append(User()) print(my_list) # [1, 3, 5, 0] my_list2 = [1] # type: List[User] print (my_list2) # hints2.py, line 14: List item 1 has incompatible type "int"
  • 33.
    Forward references class Tree: def__init__(self, left: Tree, right: Tree) -> None: self.left = left self.right = right Это не будет работать
  • 34.
    Forward references class Tree: def__init__(self, left: ‘Tree’, right: ‘Tree’) -> None: self.left = left self.right = right Это не будет работать
  • 35.
    Union from typing importUnion, Sequence class Employee(str): pass def employees(e: Union[Employee, Sequence[Employee]]) -> None: if isinstance(e, Employee): e = [e] for _ in e: print("Employee: {}".format(_)) employees(Employee("Max K")) # Employee: Max K employees([Employee("Max K"), Employee("Dima Y")]) # Employee: Max K # Employee: Dima Y
  • 36.
  • 37.
    Проверка в Runtime importtyping if typing.TYPE_CHECKING: import expensive_mod def a_func(arg: 'expensive_mod.SomeClass') -> None: a_var = arg # type: expensive_mod.SomeClass
  • 38.
    Переменные x = []# type: List[Employee] x, y, z = [], [], [] # type: List[int], List[int], List[str] x, y, z = [], [], [] # type: (List[int], List[int], List[str]) x = [ 1, 2, ] # type: List[int]
  • 39.
    Stub-files ‣ Расширения ‣ Third-partyмодули без типов ‣ Standard library модули ‣ Модули, которые должны поддерживать Python 2 и 3 ‣ Модули, которые используют аннотации в другом контексте
  • 40.
    Игнор ‣ Декоратор @no_type_checkдля класса или функции ‣ # type: ignore
  • 41.
  • 42.