Python: как разрешить "внутренней функции" изменять нелокальные переменные в нескольких "внешних функциях"
Предположим, у меня есть функция, которая имеет большой участок кода, повторяемый в разных местах внутри функции, я могу сделать следующее:
def foo():
def bar():
# do some stuff
bar()
# do some other stuff
bar()
Я могу "читать" переменные, которые находятся в области видимости foo
пока внутри bar
кроме того, если мне нужно отредактировать их, я могу сделать это:
def foo():
# stuff involving var1 and var2
def bar():
nonlocal var1, var2
# do some stuff
bar()
# do some other stuff
bar()
Эта проблема
Теперь предположим, что у меня есть несколько функций, foo_1
, foo_2
, foo_3
… И т. Д., Все из которых имеют одинаковые строки кода из bar
повторяется в них. Это было бы однообразно (не говоря уже о кошмаре каждый раз, когда я хотел изменить bar
) определить bar
внутри каждого foo_i
Однако выполнение следующих действий не работает, так как nonlocal
работает в области, в которой определена функция, а не в той, в которой она вызывается:
def bar():
nonlocal var1, var2 # SyntaxError: no binding for nonlocal 'var1' found
# do some stuff
def foo_1():
# stuff involving var1 and var2
bar()
# do some other stuff
bar()
Потенциальное решение
Один из способов обойти эту проблему - передать все переменные, которые нужно изменить, а затем вернуть их потом. Что-то вроде этого:
def bar(var1, var2):
# do some stuff
return var1, var2
def foo_1():
# stuff involving var1 and var2
var1, var2 = bar(var1, var2)
# do some other stuff
var1, var2 = bar(var1, var2)
Мой вопрос
Решение выше имеет несколько проблем:
- Это гораздо более многословно, чем просто
bar()
(особенно, когда есть больше переменных) - Это на самом деле не так уж много улучшений в определении
bar
внутри каждогоfoo_i
так как предположим, что переменная, которую я ранее только что получил вbar
Я сейчас решаю редактировать. Мне нужно не только изменить функцию, но и изменить ее везде, где она вызывается (поскольку теперь я должен возвращать дополнительную переменную).
Есть ли лучший способ достижения вышеуказанного?
(Это похоже на проблему, которая должна иметь ответ, поэтому я прошу прощения, если это повторный вопрос. Я пока не смог ничего найти.)
2 ответа
[...] кажется, что нелокальные работы в области, в которой определена функция, а не в том, что она называется [...].
Ты прав. nonlocal
применяется только к пространству имен, в котором определена указанная функция. Из документации для nonlocal
:
Нелокальный оператор заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области, исключая глобальные переменные.
(акцент мой)
Как упомянул @Aran, потенциальное решение, позволяющее избежать ненужного многословия, - заключить переменные, которые вы хотите передать, в определенные функции в классе. namedtuple
будет привлекательным выбором, так как вам не нужен полноценный класс, но, как уже было сказано, он неизменен. Вы можете использовать types.SimpleNamespace
объект вместо этого, поскольку они также легки:
from types import SimpleNamespace
def bar(n):
n.a = 3, n.b = 4
def foo():
n = SimpleNamespace(a=1, b=2)
bar(n)
print(n.a, n.b) # 3 4
Вы можете объявить эти переменные как global
перед изменением их внутри каждой функции, но их необходимо объявить перед любой функцией, которая их использует, также имейте в виду, что это изменит переменные для областей действия ЛЮБОЙ функции, в которой они объявлены как глобальные:
var1 = var2 = None
def bar():
global var1, var2
# do some stuff
def foo_1():
# stuff involving var1 and var2
bar()
# do some other stuff
bar()