Возможно ли изменить переменную в python, которая находится во внешней, но не глобальной области?
Учитывая следующий код:
def A() :
b = 1
def B() :
# I can access 'b' from here.
print( b )
# But can i modify 'b' here? 'global' and assignment will not work.
B()
A()
Для кода в функции B()
переменная b
находится во внешней области, но не в глобальной области. Можно ли изменить переменную b
из функции B()
? Конечно, я могу прочитать его здесь и print()
, но как его изменить?
Ответы
Ответ 1
Python 3.x имеет ключевое слово nonlocal
. Я думаю, что это делает то, что вы хотите, но я не уверен, что вы используете python 2 или 3.
Нелокальный оператор заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области. Это важно, потому что поведение по умолчанию для привязки - поиск локальное пространство имен. Этот оператор позволяет инкапсулированному коду перегруппировки переменных за пределами локальной области, помимо глобальных (модуль).
Для python 2 я обычно просто использую изменяемый объект (например, список или dict) и изменяю значение вместо переназначения.
Пример:
def foo():
a = []
def bar():
a.append(1)
bar()
bar()
print a
foo()
Выходы:
[1, 1]
Ответ 2
Вы можете использовать пустой класс для хранения временной области. Это похоже на изменчивое, но немного красивое.
def outer_fn():
class FnScope:
b = 5
c = 6
def inner_fn():
FnScope.b += 1
FnScope.c += FnScope.b
inner_fn()
inner_fn()
inner_fn()
Это дает следующий интерактивный вывод:
>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined
Ответ 3
Я немного новичок в Python, но я немного читал об этом. Я считаю, что лучшее, что вы получите, - это обходной путь Java, заключающийся в переносе вашей внешней переменной в список.
def A():
b = [1]
def B():
b[0] = 2
B()
print(b[0])
# The output is '2'
Изменение: Я думаю, это было правдой до Python 3. Похоже, nonlocal
ваш ответ.
Ответ 4
Нет, вы не можете, по крайней мере, таким образом.
Потому что "операция set" создаст новое имя в текущей области видимости, которое будет охватывать внешнюю область.
Ответ 5
Для тех, кто смотрит на это намного позже на более безопасном, но более тяжелом обходном пути. Без необходимости передавать переменные в качестве параметров.
def outer():
a = [1]
def inner(a=a):
a[0] += 1
inner()
return a[0]
Ответ 6
Я не думаю, что вы должны это делать. Функции, которые могут изменять вещи в их окружающем контексте, опасны, поскольку этот контекст может быть написан без знания функции.
Вы можете сделать это явным, либо сделав B открытым методом, либо C - приватным методом в классе (возможно, лучший способ); или с помощью изменяемого типа, такого как список, и передать его явно C:
def A():
x = [0]
def B(var):
var[0] = 1
B(x)
print x
A()
Ответ 7
Вы можете, но вам придется использовать глобальный статус (не очень хорошее решение, как всегда при использовании глобальных переменных, но оно работает):
def A():
global b
b = 1
def B():
global b
print( b )
b = 2
B()
A()
Ответ 8
Я не знаю, есть ли атрибут функции, которая дает __dict__
внешнего пространства функции, когда это внешнее пространство не является глобальным пространством == модуль, который имеет место, когда функция является вложенной функцией, в Python 3.
Но в Python 2, насколько мне известно, такого атрибута нет.
Таким образом, единственные возможности делать то, что вы хотите:
1) с использованием изменяемого объекта, как говорят другие
2)
def A() :
b = 1
print 'b before B() ==', b
def B() :
b = 10
print 'b ==', b
return b
b = B()
print 'b after B() ==', b
A()
результат
b before B() == 1
b == 10
b after B() == 10
,
Nota
Решение Cédric Julien имеет недостаток:
def A() :
global b # N1
b = 1
print ' b in function B before executing C() :', b
def B() :
global b # N2
print ' b in function B before assigning b = 2 :', b
b = 2
print ' b in function B after assigning b = 2 :', b
B()
print ' b in function A , after execution of B()', b
b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b
результат
global b , before execution of A() : 450
b in function B before executing B() : 1
b in function B before assigning b = 2 : 1
b in function B after assigning b = 2 : 2
b in function A , after execution of B() 2
global b , after execution of A() : 2
Глобальный b после выполнения A()
был изменен, и его нельзя
Это случай, только если есть объект с идентификатором b в глобальном пространстве имен
Ответ 9
Короткий ответ, который просто будет работать автоматически
Я создал библиотеку Python для решения этой конкретной проблемы. Это выпущено под неуважением, так что используйте это, как Вы желаете. Вы можете установить его с помощью pip install seapie
или проверить домашнюю страницу здесь https://github.com/hirsimaki-markus/SEAPIE
[email protected]:home$ pip install seapie
from seapie import Seapie as seapie
def A():
b = 1
def B():
seapie(1, "b=2")
print(b)
B()
A()
выходы
2
аргументы имеют следующее значение:
- Первый аргумент - область выполнения. 0 будет означать локальный
B()
, 1 означает родительский A()
, а 2 будет означать прародителя <module>
aka global
- Второй аргумент - это строка или кодовый объект, который вы хотите выполнить в заданной области
- Вы также можете вызывать его без аргументов для интерактивной оболочки внутри вашей программы
Длинный ответ
Это сложнее. Seapie работает путем редактирования фреймов в стеке вызовов с использованием API CPython. CPython является стандартом де-факто, поэтому большинству людей не нужно беспокоиться об этом.
Волшебные слова, которые, вероятно, вас заинтересуют, если вы читаете это, следующие:
frame = sys._getframe(1) # 1 stands for previous frame
parent_locals = frame.f_locals # true dictionary of parent locals
parent_globals = frame.f_globals # true dictionary of parent globals
exec(codeblock, parent_globals, parent_locals)
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only
Последнее заставит обновления переходить в локальную область. локальные области, однако, оптимизируются иначе, чем глобальные области, поэтому создание новых объектов имеет некоторые проблемы, когда вы пытаетесь вызывать их напрямую, если они никак не инициализируются. Я скопирую несколько способов обойти эти проблемы со страницы github
- Сбор, импорт и определение ваших объектов заранее
- Предварительно присваивать заполнители вашим объектам
- Переназначить объект в основной программе для обновления таблицы символов: x = locals() ["x"]
- Используйте exec() в основной программе вместо прямого вызова, чтобы избежать оптимизации. Вместо вызова x do: exec ("x")
Если вы чувствуете, что использование exec()
- это не то, что вы хотите использовать, вы можете
эмулируйте поведение, обновляя истинный локальный словарь (а не тот, который возвращается locals()). Я скопирую пример из https://faster-cpython.readthedocs.io/mutable.html
import sys
import ctypes
def hack():
# Get the frame object of the caller
frame = sys._getframe(1)
frame.f_locals['x'] = "hack!"
# Force an update of locals array from locals dict
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
ctypes.c_int(0))
def func():
x = 1
hack()
print(x)
func()
Выход:
hack!