В докладе раскрыты слабые места Python, проблемы с GC, функциональным стилем, реализацией стандартных структур данных и интерпретатора CPython.
Автор: Кирилл Лашкевич
Разминка
def empty(_): return None
def add(d, k, v):
def new_dict(x):
if x == k:
return v
else:
return d(x)
return new_dict
def add_f(d, k, v):
return lambda x: v if k == x else d(x)
Разминка
def empty(_): return None
>> d = add(empty, 1, "abc")
def add(d, k, v):
>> d = add_f(d, 2, "def")
def new_dict(x):
>> d(1)
if x == k:
'abc'
return v
>> d(2)
else:
'def'
return d(x) >> d(0)
>>
return new_dict
def add_f(d, k, v):
return lambda x: v if k == x else d(x)
Захват переменных в замыкания
def Counter(x):
a = x
def zero():
a = 0
def val():
return a
def inc():
a += 1
return a
return (zero,
val, inc)
Захват переменных в замыкания
def Counter(x): >> zero, val, inc = Counter(5)
a = x
>> zero()
def zero(): >> val()
a = 0
5
def val():
>> inc()
return a
def inc():
a += 1
return a
return (zero,
val, inc)
Захват переменных в замыкания
def Counter(x): >> zero, val, inc = Counter(5)
a = x
>> zero()
def zero(): >> val()
a = 0
5
def val():
>> inc()
return a
UnboundLocalError:
def inc():
a += 1
local variable 'a'
return a
referenced before
return (zero,
val, inc) assignment
3.x
def Counter(x):
a = x
def zero():
nonlocal a = 0
def val():
return a
def inc():
nonlocal a
a += 1
return a
return (zero,
val, inc)
2.x
def Counter(x):
a = [x]
def zero():
a[0] = 0
def val():
return a[0]
def inc():
a[0] += 1
return a[0]
return (zero,
val, inc)
Генераторы и yield
Возможность удобно использовать генераторы в
генераторах появилась только в 3.3, PEP 380
yield form
Даже с этим генераторы слабее более общей
концепции coroutines
yield не функция, а синтаксический элемент
привязанный к контексту, не first-class citizen
def accumulate():
res = 0
while 1:
n = yield
if n is None:
return res
res += n
def gather_sums(sums):
while 1:
s = yield from accumulate()
sums.append(s)
def accumulate():
res = 0
while 1:
n = yield
if n is None:
return res
res += n
>> sums = []
>> acc = gather_sums(sums)
>> next(acc)
>> for i in range(4):
..
acc.send(i)
..
>> acc.send(None)
>> for i in range(5):
..
acc.send(i)
..
>> acc.send(None)
>> sums
[6, 10]
def gather_sums(sums):
while 1:
s = yield from accumulate()
sums.append(s)
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
def inorder_33(t):
if t:
yield from inorder(t.left)
yield t.label
yield from inorder(t.right)
function inorder(f, t)
if t then
inorder(f, t.left)
f(t.label)
return inorder(f, t.right)
end
end
for label in coroutine.wrap(inorder),
coroutine.yield, t do
-- something with label
end
inorder(print, t)