Python imperfections
Cyril @notorca Lashkevich
Элементы функционального
стиля
Python не функциональный язык
Но иногда хочется…
… или случайно получается
Разминка
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)
Неоднородность Object Model
weakref
__slots__
__del__
new/old style objects
Bytecode
~110 инструкции байткода (39 в Lua)
PyObject *
PyEval_EvalFrameEx(PyFrameObject *f,

int throwflag) {
// ~2200 lines
}
void luaV_execute (lua_State *L) {
// 320 lines
}
API
Объемный и сложный в использовании
818 функции (3.3) vs 123 + 66 aux в Lua
Не все гладко с Py_Initialize/Py_Finalize
GIL
Глобальный для
процесса семафор/
conditional variable
Не дает нескольким
потокам выполнятся
одновременно…
…почти
Программа на С
создает потоки, в
каждом из которых
свой экземпляр
интерпретатора?
Программа на С
создает потоки, в
каждом из которых
свой экземпляр
интерпретатора?
GIL глобальный для
процесса
The End
Cyril @notorca Lashkevich

Недостатки Python

  • 1.
  • 2.
    Элементы функционального стиля Python нефункциональный язык Но иногда хочется… … или случайно получается
  • 3.
    Разминка def empty(_): returnNone
 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)
  • 4.
    Разминка def empty(_): returnNone
 >> 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)
  • 5.
    Захват переменных взамыкания def Counter(x): a = x def zero(): a = 0 def val(): return a def inc(): a += 1 return a return (zero, val, inc)
  • 6.
    Захват переменных взамыкания 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)
  • 7.
    Захват переменных взамыкания 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
  • 8.
    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)
  • 9.
    Генераторы и yield Возможностьудобно использовать генераторы в генераторах появилась только в 3.3, PEP 380 yield form Даже с этим генераторы слабее более общей концепции coroutines yield не функция, а синтаксический элемент привязанный к контексту, не first-class citizen
  • 10.
    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)
  • 11.
    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)
  • 12.
    def inorder(t):
 if t:
 forx 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)
  • 13.
    function inorder(f, t)
 ift 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)
  • 14.
  • 15.
    Bytecode ~110 инструкции байткода(39 в Lua) PyObject * PyEval_EvalFrameEx(PyFrameObject *f,
 int throwflag) { // ~2200 lines } void luaV_execute (lua_State *L) { // 320 lines }
  • 16.
    API Объемный и сложныйв использовании 818 функции (3.3) vs 123 + 66 aux в Lua Не все гладко с Py_Initialize/Py_Finalize
  • 17.
    GIL Глобальный для процесса семафор/ conditionalvariable Не дает нескольким потокам выполнятся одновременно… …почти
  • 18.
    Программа на С создаетпотоки, в каждом из которых свой экземпляр интерпретатора?
  • 19.
    Программа на С создаетпотоки, в каждом из которых свой экземпляр интерпретатора? GIL глобальный для процесса
  • 20.