Можно ли изменить переменную в 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 переменной внутри

8 63

8 ответов:

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]

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

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

Я немного новичок в Python, но я немного читал об этом. Я считаю, что лучшее, что вы получите, похоже на работу Java, которая заключается в том, чтобы обернуть Вашу внешнюю переменную в список.

def A():
   b = [1]
   def B():
      b[0] = 2
   B()
   print(b[0])

//output is '2'

Edit: я думаю, это было, вероятно, верно до Python 3. Похоже, что "нелокальный" - это ваш ответ.

нет, вы не можете, по крайней мере, таким образом.

потому что" set operation " создаст новое имя в текущей области, которая охватывает внешний.

для тех, кто смотрит на это гораздо позже, более безопасный, но более тяжелый обходной путь. Без необходимости передавать переменные в качестве параметров.

def outer():
    a = [1]
    def inner(a=a):
        a[0] += 1
    inner()
    return a[0]

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

вы можете сделать его явным, либо сделав B публичным методом, а C частным методом в классе (возможно, лучший способ); или используя изменяемый тип, такой как список, и явно передавая его в C:

def A():
    x = [0]
    def B(var): 
        var[0] = 1
    B(x)
    print x

A()

вы можете, но вам придется использовать глобальные заявления (не очень хорошее решение, как всегда при использовании глобальных переменных, но оно работает):

def A():
    global b
    b = 1

    def B():
      global b
      print( b )
      b = 2

    B()
A()

Я не знаю, есть ли атрибут функция, которая дает __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

.

Примечание

решение Седрика Жюльена имеет недостаток:

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 в глобальном пространстве имен