Доступ к переменным функции caller в Python

Мое подозрение в том, что то, что я хочу сделать, не совсем выполнимо чистым способом в Python. Вот несколько вложенных функций, которые называют друг друга. (В общем, они не должны лексически ограничены, но им необходимо динамически звонить друг другу.)

def outer() :
    s_outer = "outer\n"

    def inner() :
        s_inner = "inner\n"
        do_something()

    inner()

Теперь, когда я вызываю do_something(), я хотел бы получить доступ к переменным вызывающих функций дальше по стоп-казу, в этом случае s_outer и s_inner.

К сожалению, ключевое слово nonlocal здесь помогает мне, только если я определяю do_something() внутри функции inner(). Однако, если я определяю его на том же уровне, что и outer(), тогда ключевое слово nonlocal не будет работать.

Однако я хочу вызвать do_something() из других функций, но всегда выполняю его в соответствующем контексте и получаю доступ к соответствующим областям.

Чувствуя себя непослушным, я написал небольшой аксессуар, который я могу вызвать из do_something() следующим образом:

def reach(name) :
    for f in inspect.stack() :
        if name in f[0].f_locals : return f[0].f_locals[name]
    return None 

а затем

def do_something() :
    print( reach("s_outer"), reach("s_inner") )

отлично работает.

Мои два вопроса:

  • Есть ли лучший способ решить эту проблему? (Кроме того, чтобы обернуть соответствующие данные в dicts и передать эти dicts явно на do_something())

  • Есть ли более элегантный/укороченный способ реализации функции reach()?

Ура!

Ответ 1

То, что я закончил, было

scope = locals()

и сделать scope доступным из do_something. Таким образом, мне не нужно добираться, но я все равно могу получить доступ к словарю локальных переменных вызывающего. Это очень похоже на создание самого словаря и его передачу.

Ответ 2

Нет, и, на мой взгляд, не должно быть изящного способа реализации reach, поскольку это вводит новую нестандартную косвенность, которую действительно сложно понять, отладить, протестировать и поддерживать. Поскольку мантра Python (try import this) говорит:

Явный лучше, чем неявный.

Итак, просто передайте аргументы. Вы-из-за-будущего будете очень благодарны вам - с сегодняшнего дня.

Ответ 3

Есть ли лучший способ решить эту проблему? (Помимо обертывания соответствующих данных в dicts и передачи этих dicts явно do_something())

Передача dicts явно является лучшим способом.

То, что вы предлагаете, звучит очень необычно. Когда код увеличивается по размеру, вам нужно разбить код на модульную архитектуру с чистыми API-интерфейсами между модулями. Он также должен быть тем, что легко понять, легко объяснить и легко передать другому программисту, чтобы изменить/улучшить/отладить его. То, что вы предлагаете, похоже на то, что это не чистый API, нетрадиционный, с неочевидным потоком данных. Я подозреваю, что это, вероятно, заставит многих программистов сердиться, когда они это увидели.:)

Другой вариант - сделать функции членами класса с данными, находящимися в экземпляре класса. Это может хорошо работать, если ваша проблема может быть смоделирована как несколько функций, работающих с объектом данных.