-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTransducer.py
106 lines (80 loc) · 4.32 KB
/
Transducer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# страж для аргумента initial преобразователя Reduce
SENTINEL = object()
class Transducer:
"""Конвеер с оператором `>>` на преобразователях.
Класс `Transducer` позволяет объединять `callable` объекты в цепочку
преобразований с помощью оператора `>>`. `callable` конвертируются
в звенья типа `Transducer`, которые объединяются в цепочки.
Атрибуты:
func (callable): `callable`, соответствующий первому звену цепочки
label (str): вспомогательная строчка-бирка для `__repr__`
chain (list): упорядоченная цепочка `callable` объектов
chain_repr (list): бирки объектов цепочки
"""
def __init__(self, func=lambda x: x, label='EmptyTransducer'):
"""Создается `Transduce` по `callable` и строчке-бирке.
Без них создается пустой `Transduce`, возвращающий при вызове
то же, что получил на вход."""
self.func = func
self.label = label
self.chain = [self]
self.chain_repr = [label]
def __call__(self, s):
"""Объект `Transducer` является `callable` и возвращают результат
вызова завеонутого в него `callable` от одного аргумента"""
return self.func(s)
def __rshift__(self, other):
"""Реализация работы оператора `>>` (левого)
`Transducer >> not_callable` поднимает `TypeError`"""
if not callable(other):
raise TypeError
return Transducer.concat(self, Transducer.from_callable(other))
def __rrshift__(self, other):
"""Реализация работы оператора `>>` (правого)
`Transducer >> not_callable` сворачивается, вычисляя результат"""
if not callable(other):
for func in self.chain:
other = func(other)
return other
return Transducer.concat(Transducer.from_callable(other), self)
@staticmethod
def concat(Tr1, Tr2):
"""Вспомогательный метод для склеивания двух `Transducer`"""
Tr = Transducer(Tr1.func, Tr1.label)
Tr.chain = Tr1.chain + Tr2.chain
Tr.chain_repr = Tr1.chain_repr + Tr2.chain_repr
return Tr
@staticmethod
def from_callable(clb):
"""Вспомогательный метод, гарантирующий, что аргументами
`concat` будут `Transducer` объекты"""
if isinstance(clb, Transducer):
return clb
return Transducer(clb, str(clb))
def __repr__(self):
"""Отображаем цепочку `Transduce` в порядке применения"""
return 'input >> ' + ' >> '.join(self.chain_repr)
def Call(func):
"""Преобразователь, применяющий `func` к аргументу"""
return Transducer(func, 'Call({})'.format(str(func)))
def Map(func):
"""Преобразователь, реализующий функциональность встроенной `map`"""
def map_(s):
return (func(x) for x in s)
return Transducer(map_, 'Map({})'.format(str(func)))
def Filter(func=bool):
"""Преобразователь, реализующий функциональность встроенной `filter`"""
def filter_(s):
return (x for x in s if func(x))
return Transducer(filter_, 'Filter({})'.format(str(func)))
def Reduce(func, initial=SENTINEL):
"""Преобразователь, реализующий функциональность `functools.reduce`"""
default = (initial == SENTINEL)
def reduce_(s):
iter_s = iter(s)
result = next(iter_s) if default else initial
for x in iter_s:
result = func(result, x)
return result
args_repr = str(func) if default else ', '.join((str(func), str(initial)))
return Transducer(reduce_, args_repr)