Есть ли в Python способ управления областью видимости, чтобы переменные в вызывающих функциях были видны внутри вызываемых функций? Я хочу что-то вроде следующего

z = 1

def a():
    z = z * 2
    print z

def b():
    z = z + 1
    print z
    a()
    print z

b()

Я бы хотел получить следующий результат

2
4
2

Реальное решение этой проблемы - просто передать z как переменную. Я не хочу этого делать.

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

9
MRocklin 4 Фев 2013 в 21:27

5 ответов

Лучший ответ

Я думаю, это то, что вы ищете. Если вы управляете внутренними функциями, а искомая переменная уже находится в области действия в цепочке вызовов, но вы не хотите, чтобы ваши пользователи включали эту переменную в сам вызов. Вам явно не нужно ключевое слово global, потому что вы не хотите, чтобы внутренние назначения влияли на внешнюю область.

Использование модуля inspect позволяет получить доступ к контексту вызова. (Но имейте в виду, что это несколько хрупко. Возможно, вы должны использовать только для CPython без резьбы.)

import inspect

def calling_scope_variable(name):
  frame = inspect.stack()[1][0]
  while name not in frame.f_locals:
    frame = frame.f_back
    if frame is None:
      return None
  return frame.f_locals[name]

z = 1

def a():
  z = calling_scope_variable('z')
  z = z * 2
  print z

def b():
  z = calling_scope_variable('z')
  z = z + 1
  print z
  a()
  print z

b()

Это дает правильный ответ:

2
4
2
7
John Hazen 4 Фев 2013 в 19:40

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

def a(z):
    z = z * 2
    print z

def b():
    global z
    z = z + 1
    print z
    a(z)
    print z

b()

Вы можете определить a() в b() и сделать что-то подобное:

def b():
    global z
    z = z + 1
    print z
    def a():
        z = z * 2
        print z
    a()
    print z

Однако это не совсем оптимально.

2
Gareth Latty 4 Фев 2013 в 19:25

Это немного уродливо, но избегает использования глобалов, и я проверил это, и это работает. Используя код из выбранного ответа, можно найти здесь функция getSetZ() имеет «статическую» переменную, которую можно использовать для хранения переданного ей значения, а затем извлечения при вызове функции с None в качестве параметра. Некоторые ограничения заключаются в том, что предполагается, что None не является возможным значением для z, и что вы не используете потоки. Вам просто нужно не забывать вызывать getSetZ() непосредственно перед каждым вызовом функции, для которой вы хотите, чтобы z вызывающей функции был доступен, и чтобы получить значение из getSetZ() и поместить его в локальной переменной в этой функции.

def getSetZ(newZ):
    if newZ is not None:
        getSetZ.z = newZ
    else:
        return getSetZ.z

def a():
    z = getSetZ(None)
    z = z * 2
    print z

def b():
    z = getSetZ(None)
    z = z + 1
    print z
    getSetZ(z)
    a()
    print z

getSetZ.z = 0
getSetZ(1)
b()

Надеюсь, это поможет.

2
Community 23 Май 2017 в 12:30

Я не совсем понимаю, чего вы пытаетесь достичь, но это печатает 2, 4, 2:

z = 1

def a():
    global z
    y = z * 2
    print y

def b():
    global z
    z = z + 1
    print z
    a()
    print z

b()
2
Ronan Lamy 4 Фев 2013 в 18:25

Это может быть правильным местом для класса:

class MyClass(object):
    def __init__(self, z):
        self.z = 1

    def a(self):
        self.z = self.z * 2
        print self.z

    def b():
        self.z = self.z + 1
        print self.z
        self.a()
        print self.z

Вы можете получить тот же эффект с глобальными переменными, но я бы предложил создать ваше собственное отдельное пространство имен (например, с классом) и использовать его для своей области видимости.

5
Kirk Strauser 4 Фев 2013 в 17:40