Область видимости и замыкания в Python
Прежде чем переменной можно будет воспользоваться, ей необходимо присвоить значения.
Область видимости переменной определяется местом, где ей было присвоено значение.
Правило LEGB
Любое присвоение переменной в функции создает новую локальную переменную
Если необходимо изменить глобальную переменную необходимо сначала указать ее с инструкцией global
Переменные global сразу будут искать в глобальном пространстве, миную локальное и внешнее.
Если необходимо изменить переменную из внешней функции, то нужна инструкция nonlocal
Если переменной было присвоено значение внутри функции, то обратится к глобальным уже не получится
Если необходимо изменять переменную из внешней функции, то нужно использовать инструкцию nonlocal
Но иногда такое поведения бывает не желательно
Обращения к i и соответственно получения его значения происходит во время вызова функции, в этот момент i уже имеет значения 4.
Это можно исправить присвоив значению по умолчанию, так как оно вычисляется в момент создание вложенной функции
А так как значения по умолчанию присваивается во время создании функции, то это значения будет общим для всех вызовов
Область видимости переменной определяется местом, где ей было присвоено значение.
Правило LEGB
- Local локальная область функции
- Enclosing область внешней функции
- Global глобальное пространство
- Built-in встроенном (модуль builtis)
Если не указаны инструкции, то поиск переменных по пространствам имен происходит с низу верх
local->enclosing->global->built-in
local->enclosing->global->built-in
x = 10 print('Global x =', x) def fun_enclosing(): print('Enclosing x =', x) def fun_local(): print('Local x =', x) fun_local() fun_enclosing()
Global x = 10 Enclosing x = 10 Local x = 10
Любое присвоение переменной в функции создает новую локальную переменную
x = 10 print('Global x =', x) def fun_enclosing(): x = 20 # создания новой переменной в пространстве имен fun_enclosing print('Enclosing x =', x) def fun_local(): print('Local x =', x) fun_local() fun_enclosing()
Global x = 10 Enclosing x = 20 Local x = 20
Если необходимо изменить глобальную переменную необходимо сначала указать ее с инструкцией global
x = 10 print('Global x =', x) def fun_enclosing(): global x x = 20 print('Enclosing x =', x) def fun_local(): print('Local x =', x) fun_local() fun_enclosing() print('Global after x =', x)
Global x = 10 Enclosing x = 20 Local x = 20 Global after x = 20
Переменные global сразу будут искать в глобальном пространстве, миную локальное и внешнее.
x = 10 print('Global x =', x) def fun_enclosing(): x = 20 print('Enclosing x =', x) def fun_local(): global x print('Local x =', x) fun_local() fun_enclosing() print('Global after x =', x)
Global x = 10 Enclosing x = 20 Local x = 10 Global after x = 10
Если необходимо изменить переменную из внешней функции, то нужна инструкция nonlocal
x = 10 print('Global x =', x) def fun_enclosing(): x = 20 print('Enclosing x =', x) def fun_local(): nonlocal x x = 30 print('Local x =', x) fun_local() print('Enclosing after x =', x) fun_enclosing() print('Global after x =', x)
Global x = 10 Enclosing x = 20 Local x = 30 Enclosing after x = 30 Global after x = 10
Если переменной было присвоено значение внутри функции, то обратится к глобальным уже не получится
x = 10 def fun_local(): print(x) x = 5 fun_local()
UnboundLocalError: local variable 'x' referenced before assignment
Замыкания
Это функция у которой есть ссылки на переменные из внешней функции. Такие переменные называются свободными(freevars)def make_multiplier_of(n): """принимает n и создает функцию(multiplier) которая будет умножать n на аргумент(x) из созданной функции""" def multiplier(x): """у объекта multiplier будет ссылка на переменную n, к которой она сможет обращаться уже после создания""" return x * n return multiplier times3 = make_multiplier_of(3) times5 = make_multiplier_of(5) print(times3(9)) print(times5(3)) print(times5(times3(2))) # ---- print('все переменные: ', times3.__code__.co_varnames) print('свободные из внешней функции: ', times3.__code__.co_freevars) for indx, fv_name in enumerate(times3.__code__.co_freevars): print('{0} = {1}'.format(fv_name, times3.__closure__[indx].cell_contents)) print('все переменные: ', times5.__code__.co_varnames) print('свободные из внешней функции: ', times5.__code__.co_freevars) for indx, fv_name in enumerate(times5.__code__.co_freevars): print('{0} = {1}'.format(fv_name, times5.__closure__[indx].cell_contents))
27 15 30 все переменные: ('x',) свободные из внешней функции: ('n',) n = 3 все переменные: ('x',) свободные из внешней функции: ('n',) n = 5
Если необходимо изменять переменную из внешней функции, то нужно использовать инструкцию nonlocal
def make_averanger(): count = 0 total = 0 def averanger(new_value): nonlocal count, total count += 1 total += new_value return total / count return averanger avg = make_averanger() print(avg(10)) print(avg(20)) print(avg(100))
Но иногда такое поведения бывает не желательно
def makeActions(): acts = [] for i in range(5): acts.append(lambda x: i ** x) return acts for act in makeActions(): print(act(1), end=', ')
4, 4, 4, 4, 4
Это можно исправить присвоив значению по умолчанию, так как оно вычисляется в момент создание вложенной функции
def makeActions(): acts = [] for i in range(5): acts.append(lambda x, i=i: i ** x) return acts for act in makeActions(): print(act(1), end=', ') print() def makeActions(): acts = [] for i in range(5): def func(x, i=i): return x * i acts.append(func) return acts for act in makeActions(): print(act(1), end=', ')
0, 1, 2, 3, 4 0, 1, 2, 3, 4
А так как значения по умолчанию присваивается во время создании функции, то это значения будет общим для всех вызовов
def func(v, x=[]): x.append(v) print(x) for i in range(5): func(i)
[0] [0, 1] [0, 1, 2] [0, 1, 2, 3] [0, 1, 2, 3, 4]
Комментарии
Отправить комментарий