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()
Другие вопросы по тегам