воскресенье, 1 марта 2020 г.

ЛМНты Python, 46 - 50

>>> Нелокальное имя

Имя x, созданное операцией присваивания в начале функции fun, внутри вложенной функции fun2 экранируется другим именем x:

>>> def fun():
...     x = 'fun'
...     def fun2():
...         x = 'fun2'
...         print(x)
...     fun2()
...     print(x)
...
>>> fun()
fun2
fun

Чтобы внутри функции fun2 можно было работать с именем x из пространства имен объемлющей функции, имя x нужно объявить нелокальным:

>>> x = 'global x'
>>>
>>> def fun():
...     x = 'fun'
...     def fun2():
...         nonlocal x
...         x = 'fun2'
...         print(x)
...     fun2()
...     print(x)
...
>>> fun()
fun2
fun2

>>> Имена убивают

Идентификаторы в Python "возникают" и связываются с объектами не только при присваивании, но также при определении функции, класса и в некоторых других случаях. Независимо от способа возникновения, их видимость и доступность подчиняются одним и тем же правилам.

Имя функции fun внутри функции xyz экранирует глобальное имя fun:

>>> def fun():
...     print('global fun')
...
>>> def xyz():
...     def fun():
...         print('inner fun')
...     fun()
...
>>> xyz()
inner fun
>>> fun()
global fun

Объявление fun глобальным внутри функции xyz позволяет переопределить глобальную функцию fun:

>>> def fun():
...     print('global fun')
...
>>> def xyz():
...     global fun
...     def fun():
...         print('inner fun')
...     fun()
...
>>> xyz()
inner fun
>>> fun()
inner fun

>>> Имена называют

Вот полный список случаев, когда в Python создаются идентификаторы и связываются с объектом:

  • присваивание,
  • предложение import,
  • определение класса,
  • определение функции,
  • передача аргументов при вызове функции,
  • имена в заголовке цикла for,
  • имена в предложении with после as,
  • имена в конструкции except после as.

Несколько примеров:

>>> # math связывается с модулем
... import math
>>> globals()['math']
<module 'math' (built-in)>
>>>
>>> # Cls связывается с классом
>>> class Cls(object):
...     pass
...
>>> globals()['Cls']
<class '__main__.Cls'>
>>>
>>> # i связывается с числом
... for i in (0, 1):
...     pass
...
>>> globals()['i']
1

>>> Свободные или бесплатные?

Рассмотрим функцию, возвращающую параметризованную функцию. Пусть обе функции выводят на печать свои локальные пространства имен:

>>> def make_add(n):
...     print(locals())
...     def add(x):
...         print(locals())
...         return n + x
...     return add
...
>>> add2 = make_add(2)
{'n': 2}
>>>
>>> add2(5)
{'x': 5, 'n': 2}
7

В локальном пространстве имен функции add2 имеется имя n, которое, хотя и было доступно внутри функции add во время выполнения make_add, но не определено внутри функции add непосредственно!

Переменные, используемые внутри функции, но определенные не в ней, а в одном из объемлющих локальных пространств имен, называются бесплатными переменными (free variables) и доступны через локальное пространство имен функции. А функция, использующая бесплатную переменную, называется замыканием (closure).

>>> Разоблачение замыканий

Существование вложенных функций обуславливает существование нелокальных переменых, то есть, переменных определенных в одной из объемлющих функций и доступных внутри вложенной функции.

Существование функций-замыканий есть логическое следствие "первоклассности" функций в Python (functions are first-class citizens). Так как функция, созданная внутри другой функции, может быть возвращена этой другой функцией в качестве результата, то она должна либо потерять связь с нелокальными переменными, либо запомнить, или замкнуть их внутри себя. В результате получается замыкание с бесплатными переменными.

Лямбда-функции так же замыкают нелокальные переменные, как и обычные функции:

>>> def make_add(n):
...     add = lambda x: n + x
...     return add
...
>>> add3 = make_add(3)
>>> add5 = make_add(5)
>>>
>>> add3(-2)
1
>>> add5(2)
7

Комментариев нет:

Отправить комментарий