Мир Python функционалим с помощью библиотек
Transcript of Мир Python функционалим с помощью библиотек
Мир Python: функционалим с помощью библиотек
@sapronovalex92
http://pynsk.ru
vk.com/pynsk
facebook.com/PyNskCom
@py_nsk
Александр Сапронов:
О чем сегодня?
● Зловещая болезнь “ФП”● Признаки болезни● Пилюли● Как лечить
2
Кто такой ФП и зачем
ФП - функциональное программирование
ФП подходит для задач:
3
Кто такой ФП и зачем
Где есть многопоточность:
ФП подходит для задач:
● Пока делаем запрос в Интернет - дробим числа● В одном потоке обновляем GUI, в другом счетчики● Алгоритмы “Разделяй и властвуй”
ФП - функциональное программирование4
Кто такой ФП и зачем
Где есть многопоточность:
ФП подходит для задач:
● Пока делаем запрос в Интернет - дробим числа● В одном потоке обновляем GUI, в другом счетчики● Алгоритмы “Разделяй и властвуй”
5
Кто такой ФП и зачем
Где есть многопоточность
ФП подходит для задач:
Где нужен DSL:● Конфигурации (yaml…)● Конфиги-зависимости (gem…)● Системы тестирования
6
Кто такой ФП и зачем
Где есть многопоточность
ФП подходит для задач:
Где нужен DSL
● А это почти все задачи
Где нет необходимости хранить сложное состояние
7
Кто такой ФП и зачем
Где есть многопоточность
ФП подходит для задач:
Где нужен DSLГде нет необходимости хранить сложное состояниеГде хитрая бизнес-логика
8
Признаки болезни
● Функции высшего порядка● Замыкания● Иммутабельность● Чистые функции● Ленивость● Хвостовая рекурсия● Алгебраические типы данных● Pattern mathing
9
Признаки болезни
● Функции высшего порядка● Замыкания● Иммутабельность● Чистые функции● Ленивость● Хвостовая рекурсия● Алгебраические типы данных● Pattern mathing
Узнаем про это
Лень, но и это посмотрим
А еще вот это
10
Пилюли
fn.py (https://github.com/kachayev/fn.py) Python - стандартные библиотеки
funcy (https://github.com/Suor/funcy)
hask (https://github.com/billpmurphy/hask)
11
fn.py - Алексей КачаевВозможности:
● Scala-style лямбды
● Персистентные структуры
● Бесконечные последовательности
● Каррирование
● map, filter, reduce (одинаковые для 2.*, 3.*)
● Много еще всякого
12
Pipe:
from fn import F, _from fn.iters import filter, range
func = F() >> (filter, _ < 6) >> sumassert func(range(10)) == 15
>>> from fn.func import curried>>> @curried... def sum5(a, b, c, d, e):... return a + b + c + d + e...>>> sum5(1)(2)(3)(4)(5)15>>> sum5(1, 2, 3)(4, 5)15
Каррирование
fn.py (Алексей Качаев, Киев)
13
fn.py (Алексей Качаев, Киев)
from fn import Stream
s = Stream() << [1,2,3,4,5]assert list(s) == [1,2,3,4,5]assert s[1] == 2assert list(s[0:2]) == [1,2]
s = Stream() << range(6) << [6,7]assert list(s) == [0,1,2,3,4,5,6,7]
def gen(): yield 1 yield 2 yield 3
s = Stream() << gen << (4,5)assert list(s) == [1,2,3,4,5]
from fn.monad import Option request = dict(url="face.png", mimetype="PNG")tp = Option \ .from_value(request.get("type", None)) .or_call(from_mimetype, request) .or_call(from_extension, request) .get_or("application/undefined")
# check "type" key first# or.. check "mimetype" key# or... get "url" and check extension
14
funcy (Александр Щепановский, Красноярск)
● Sequences● Collections● Functions● Decorators● Flow● String utils● Calculation● Type testing● Objects● Debugging● Primitives
Много готового кода, который хорошо написан
15
funcy (Александр Щепановский, Красноярск)
● Sequences● Collections● Functions● Decorators● Flow● String utils● Calculation● Type testing● Objects● Debugging● Primitives
Inspired by clojure, underscore and my own abstractions.
16
funcy (Александр Щепановский, Красноярск)
● Sequences● Collections● Functions● Decorators● Flow● String utils● Calculation● Type testing● Objects● Debugging● Primitives
Inspired by clojure, underscore and my own abstractions.
17
merge(coll1, coll2, coll3, ...)join(colls)merge_with(sum, dict1, dict2, ...)
funcy (Александр Щепановский, Красноярск)
walk(str.upper, {'a', 'b'}) # {'A', 'B'}walk(reversed, {'a': 1, 'b': 2}) # {1: 'a', 2: 'b'}walk_keys(double, {'a': 1, 'b': 2}) # {'aa': 1, 'bb': 2}walk_values(inc, {'a': 1, 'b': 2}) # {'a': 2, 'b': 3}
select(even, {1,2,3,10,20}) # {2,10,20}select(r'^a', ('a','b','ab','ba')) # ('a','ab')select_keys(callable, {str: '', None: None}) # {str: ''}compact({2, None, 1, 0}) # {1,2}
18
hask (Bill Murphy, New York City)
● Система типизации Hindley-Milner
● Легко создавать АТД● Pattern matching● Композиции функций● Каррирование● Ленивость● Иммутабельность● Много другого
hask - попытка привнести возможности
Haskell в Python
19
hask (Bill Murphy, New York City)
● Система типизации Hindley-Milner
● Легко создавать АТД● Pattern matching● Композиции функций● Каррирование● Ленивость● Иммутабельность● Много другого
hask - попытка привнести возможности
Haskell в Python
20
hask (Bill Murphy, New York City)
>>> L[1, 2, 3]L[1, 2, 3]
>>> my_list = ["a", "b", "c"]>>> L[my_list]L['a', 'b', 'c']
>>> L[(x**2 for x in range(1, 11))]L[1 ... ]
Ленивый список
@sig(H/ int >> int >> int)def add(x, y): return x + y
Типизация
>>> from hask import _t
>>> _t(1)int
>>> _t(Just("soylent green"))(Maybe str)
>>> _t(Right(("a", 1)))(Either a (str, int))
21
А теперь к “болезни”
Ну наконец-то (с) Цитаты Великих Людей
иммутабельность, ленивостьалгебраические типы данных, pattern mathing
22
ЛенивостьОткладывать вычисление до тех пор, пока не понадобится результат
23
ЛенивостьОткладывать вычисление до тех пор, пока не понадобится результат
Для справки:В Python3 многие методы стали возвращать
итераторы, а не готовые данные
Память расходуется по необходимости (при запросе), что тоже можно назвать ленивым поведением
24
Ленивость на чистом Pythondef lazy_property(fn): """Decorator that makes a property lazy-evaluated.""" attr_name = '_lazy_' + fn.__name__
@property def _lazy_property(self): if not hasattr(self, attr_name): setattr(self, attr_name, fn(self)) return getattr(self, attr_name) return _lazy_property
class Person(object): def __init__(self, name, occupation): self.name = name self.occupation = occupation
@lazy_property def relatives(self): relatives = "Many relatives." return relatives
def main(): Jhon = Person('Jhon', 'Coder') print("Name: {0} Occupation: {1}".format(Jhon.name, Jhon.occupation)) print("Before we access `relatives`:") print(Jhon.__dict__) print("Jhon's relatives: {0}".format(Jhon.relatives)) print("After we've accessed `relatives`:") print(Jhon.__dict__)
main()
### OUTPUT #### Name: Jhon Occupation: Coder# Before we access `relatives`:# {'name': 'Jhon', 'occupation': 'Coder'}# Jhon's relatives: Many relatives.# After we've accessed `relatives`:# {'_lazy_relatives': 'Many relatives.', 'name': 'Jhon', 'occupation': 'Coder'}
25
Лень + funcy
Не поддерживает!
26
Лень + funcy
А точнее:использует итераторы из Python
27
Лень + fn.py
Ленивые Streams
28
Лень + fn.py
Ленивые StreamsПо сути это итераторы
29
Лень + fn.py
from fn import Stream
from fn.iters import take, drop, map
from operator import add
f = Stream()
fib = f << [0, 1] << map(add, f, drop(1, f))
assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]
assert fib[20] == 6765
assert list(fib[30:35]) == [832040,1346269,2178309,3524578,5702887]
Ленивое вычисление чисел Фибоначчи
30
Лень + fn.py
class _StreamIterator(object): __slots__ = ("_stream", "_position")
def __init__(self, stream): self._stream = stream self._position = -1
def __next__(self): self._position += 1 if (len(self._stream._collection) > self._position or self._stream._fill_to(self._position)): return self._stream._collection[self._position]
raise StopIteration()
if version_info[0] == 2: next = __next__
Реализация Stream - https://github.com/kachayev/fn.py/blob/master/fn/stream.py
31
Лень + fn.py
class Stream(object): class _StreamIterator(object):
def __iter__(self): return self._StreamIterator(self) ...
Реализация Stream - https://github.com/kachayev/fn.py/blob/master/fn/stream.py
Stream возвращает значения в виде итераторов
А сам итератор возвращает настоящее значение
32
Лень + hask
Ленивые списки L[ ]>>> L[(x**2 for x in range(1, 11))]
L[1 ... ]
33
Лень + hask
Ленивые списки L[ ]Реализован примерно также, как и в fn.py
34
Лень + haskdef __getitem__(self, lst): if isinstance(lst, tuple) and len(lst) < 5 and \ any((Ellipsis is x for x in lst)): # L[x, ...] if len(lst) == 2 and lst[1] is Ellipsis: return enumFrom(lst[0]) # L[x, y, ...] elif len(lst) == 3 and lst[2] is Ellipsis: return enumFromThen(lst[0], lst[1]) # L[x, ..., y] elif len(lst) == 3 and lst[1] is Ellipsis: return enumFromTo(lst[0], lst[2]) # L[x, y, ..., z] elif len(lst) == 4 and lst[2] is Ellipsis: return enumFromThenTo(lst[0], lst[1], lst[3]) raise SyntaxError("Invalid list comprehension: %s" % str(lst)) elif hasattr(lst, "next") or hasattr(lst, "__next__"): return List(tail=lst) return List(head=lst)
Реализация синтаксиса“бесконечного” списка
35
Ленивая пауза
36
Pattern matchingCопоставление с образцом
?
37
Pattern matchingCопоставление с образцом
● Сравнение с точным значением
● Учет внутренней структуры объекта
● Сопоставление со строкой (regex)
38
PM + PEP 443>>> @fun.register(int)
... def _(arg, verbose=False):
... if verbose:
... print("Strength in numbers, eh?", end=" ")
... print(arg)
...
>>> @fun.register(list)
... def _(arg, verbose=False):
... if verbose:
... print("Enumerate this:")
... for i, elem in enumerate(arg):
... print(i, elem)
39
PM + patternsfrom patterns import patterns, Mismatch
@patterns
def factorial():
if 0: 1
if n is int: n * factorial(n-1)
if []: []
if [x] + xs: [factorial(x)] + factorial(xs)
if {'n': n, 'f': f}: f(factorial(n))
assert factorial(5) == 120
assert factorial([3,4,2]) == [6, 24, 2]
assert factorial({'n': [5, 1], 'f': sum}) == 121
factorial('hello') # raises Mismatch
Как работает?-
Парсит ASThttps://github.
com/Suor/patterns/blob/master/patterns/__init__.py
40
PM + haskdef fib(x):
return ~(caseof(x)
| m(0) >> 1
| m(1) >> 1
| m(m.n) >> fib(p.n - 1) + fib(p.n - 2))
>>> fib(1)
1
>>> fib(6)
13
Как работает?-
Организует свой стэк и анализирует frame
41
PM + haskdef pattern_match(value, pattern, env=None): """ Pattern match a value and a pattern. Args: value: the value to pattern-match on pattern: a pattern, consisting of literals and/or locally bound variables env: a dictionary of local variables bound while matching Returns: (True, env) if the match is successful, and (False, env) otherwise Raises: SyntaxError, if a variable name is used multiple times in the same pattern """ env = {} if env is None else env if isinstance(pattern, PatternMatchBind): if pattern.name in env: raise SyntaxError("Conflicting definitions for %s" % pattern.name) env[pattern.name] = value return True, env…. return False, env
42
PM + fn.py
Не поддерживает!
43
ИммутабельностьИммутабельность - это невозможность изменения состояния объекта с момента его создания.
“Строки is immutable”
(‘tuple is immutable’, )
44
Иммутабельность + fn.py
Множество готовых персистентных структур
LinkedList, Stack, Queue, Deque, ...
45
Иммутабельность + fn.py>>> from fn.immutable import SkewHeap
>>> s1 = SkewHeap(10)
>>> s2 = s1.insert(20)
>>> s2
<fn.immutable.heap.SkewHeap object at 0x10b14c050>
>>> s3 = s2.insert(30)
>>> s3
<fn.immutable.heap.SkewHeap object at 0x10b14c158> # <-- other object
>>> s3.extract()
(10, <fn.immutable.heap.SkewHeap object at 0x10b14c050>)
>>> s3.extract() # <-- s3 isn't changed
(10, <fn.immutable.heap.SkewHeap object at 0x10b11c052>)46
Иммутабельность + funcy
Не поддерживает!
47
Иммутабельность + hask
Иммутабельный список L[ ]
48
Иммутабельность + hask
def __add__(self, other): """ (+) :: [a] -> [a] -> [a] """ unify(self.__type__(), typeof(other)) if self.__is_evaluated and other.__is_evaluated: return List(head=self.__head + other.__head) elif self.__is_evaluated and not other.__is_evaluated: return List(head=self.__head + other.__head, tail=other.__tail) return List(head=self.__head, tail=itertools.chain(self.__tail, iter(other)))
Как работает?-
Персистентный список.
При добавлении элемента возвращает всегда новый список
49
Питоны кончились…НЕТ! Еще нет
50
Система типизации в hask
class Hask(object):
"""
Base class for objects within hask.
ADTs, TypedFunc, List, Undefined, and other hask-related types are all
subclasses of Hask.
All subclasses must define __type__, which returns a representation of the
object in the internal type system language.
"""
def __type__(self):
raise TypeError()
51
Система типизации в haskdef typeof(obj):
"""
Returns the type of an object within the internal type system.
Args:
obj: the object to inspect
Returns:
An object representing the type in the internal type system language
(i.e., a TypeOperator or TypeVariable)
"""
TypeVariable.next_var_name = 'a'
if isinstance(obj, Hask):
return obj.__type__()
elif isinstance(obj, tuple):
return Tuple(map(typeof, obj))
elif obj is None:
return TypeOperator(None, [])
elif type(obj) in __python_function_types__:
return TypeOperator(PyFunc, [])
return TypeOperator(type(obj), []) 52
53
vk.com/pynsk
facebook.com/PyNskCom
PyNSK контакты: Мои контакты:
ru.linkedin.com/in/alexsapronov
Питоны кончились…Вопросы?
http://pynsk.ru