42

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

  • Upload
    webcamp

  • View
    372

  • Download
    2

Embed Size (px)

Citation preview

‣ 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

Это не будет работать

Unionfrom 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