Краткое описание правил определения объема? - PullRequest
438 голосов
/ 15 ноября 2008

Что точно являются правилами области видимости Python?

Если у меня есть код:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Где x найдено? Некоторые возможные варианты включают список ниже:

  1. В прилагаемом исходном файле
  2. В пространстве имен класса
  3. В определении функции
  4. В индексной переменной цикла for
  5. Внутри для цикла

Также существует контекст во время выполнения, когда функция spam передается куда-то еще. А может лямбда-функции проходят немного по-другому?

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

Ответы [ 8 ]

385 голосов
/ 15 ноября 2008

На самом деле, краткое правило для разрешения Python Scope, от Learning Python, 3-е. Ed. . (Эти правила относятся к именам переменных, а не атрибутам. Если вы ссылаетесь на них без точки, эти правила применяются)

Правило LEGB.

L , Local - Имена, назначенные любым способом в функции (def или lambda)), и не объявленные глобальными в этой функции.

E , Локальные элементы функции включения - Имя в локальной области действия любых статически включающих функций (def или lambda), от внутреннего к внешнему.

G , Global (module) - Имена, назначенные на верхнем уровне файла модуля, или путем выполнения оператора global в def внутри файла.

B , Встроенный (Python) - Имена, предварительно назначенные в модуле встроенных имен: open, range, SyntaxError, ...

Итак, в случае

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Цикл for не имеет собственного пространства имен. В порядке LEGB, объемы будут

L: местный, в def spamcode3, code 4, code5).

E: Закрытая функция, любые включающие функции (если весь пример был в другом def)

G: Глобальный. Были ли в модуле глобально объявлены x (code1)?

B: Любой встроенный x в Python.

x никогда не будет найдено в code2 (даже в тех случаях, когда вы ожидаете, см. ответ Антти или здесь ).

148 голосов
/ 16 ноября 2008

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

В вашем примере есть только 3 области, где x будет искать в:

  • область спама - содержит все, что определено в code3 и code5 (а также code4, переменная вашего цикла)

  • Глобальная область видимости - содержит все, что определено в code1, а также Foo (и все, что меняется после него)

  • Пространство имен встроенных. Немного особый случай - он содержит различные встроенные функции и типы Python, такие как len () и str (). Как правило, это не должно быть изменено каким-либо пользовательским кодом, поэтому ожидайте, что он будет содержать стандартные функции и ничего больше.

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

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

Ограничения:

Доступны переменные в областях, отличных от переменных локальной функции, но они не могут быть привязаны к новым параметрам без дополнительного синтаксиса. Вместо этого присваивание создаст новую переменную local вместо воздействия на переменную в родительской области. Например:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Чтобы действительно изменить привязки глобальных переменных из области действия функции, необходимо указать, что переменная является глобальной с ключевым словом global. Например:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

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

102 голосов
/ 05 мая 2014

Не было никакого подробного ответа относительно времени Python3, поэтому я сделал ответ здесь.

Как указано в других ответах, есть 4 основных области, LEGB, для Local, Enclosing, Global и Builtin. В дополнение к ним существует специальная область видимости, тело класса , которая не содержит включающей области видимости для методов, определенных в классе; любые присваивания в теле класса приводят к тому, что переменная будет связана в теле класса.

В частности, оператор блока no , кроме def и class, создает переменную область видимости. В Python 2 понимание списка не создает область видимости переменной, однако в Python 3 переменная цикла в пределах понимания списка создается в новой области видимости.

Для демонстрации особенностей тела класса

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Таким образом, в отличие от тела функции, вы можете переназначить переменную с тем же именем в теле класса, чтобы получить переменную класса с тем же именем; дальнейшие поиски по этому имени разрешают вместо переменной класса.


Одним из самых больших сюрпризов для многих новичков в Python является то, что цикл for не создает переменную область видимости. В Python 2 понимание списка также не создает область действия (в то время как генераторы и вычисления dict делают это!) Вместо этого они пропускают значение в функции или глобальной области видимости:

>>> [ i for i in range(5) ]
>>> i
4

Понимания могут использоваться как хитрый (или ужасный, если хотите) способ создания изменяемых переменных в лямбда-выражениях в Python 2 - лямбда-выражение создает переменную область видимости, как это делает оператор def, но внутри лямбда-выражения заявления не допускаются. Назначение, являющееся оператором в Python, означает, что никакие переменные назначения в лямбде не допускаются, но понимание списка является выражением ...

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


Глобальный действительно означает область видимости модуля; основным модулем Python является __main__; все импортированные модули доступны через переменную sys.modules; чтобы получить доступ к __main__, можно использовать sys.modules['__main__'] или import __main__; это вполне приемлемо для доступа и назначения атрибутов там; они будут отображаться как переменные в глобальной области видимости основного модуля.


Если имя когда-либо назначено в текущей области (за исключением области класса), оно будет считаться принадлежащим этой области, в противном случае оно будет считаться принадлежащим какой-либо включающей области, которая назначается переменной (это еще не назначен или вообще не назначен) или, наконец, глобальный охват. Если переменная считается локальной, но она еще не установлена ​​или была удалена, чтение значения переменной приведет к UnboundLocalError, который является подклассом NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

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

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Это также возможно, даже если оно было затенено в прилагаемой области видимости:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

В python 2 нет простого способа изменить значение в рамках объема; обычно это моделируется изменяемым значением, таким как список с длиной 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Однако в Python 3 nonlocal приходит на помощь:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

Любая переменная, которая не считается локальной для текущей области или любой включающей области, является глобальной переменной. Глобальное имя ищется в глобальном словаре модуля; если он не найден, глобальный объект ищется из встроенного модуля; название модуля было изменено с python 2 на python 3; в питоне 2 он был __builtin__, а в питоне 3 он теперь называется builtins. Если вы назначите атрибут встроенного модуля, он будет виден после этого любому модулю как читаемая глобальная переменная, если только этот модуль не затеняет их своей глобальной переменной с тем же именем.


Чтение встроенного модуля также может быть полезно; Предположим, что вам нужна функция печати в стиле Python 3 в некоторых частях файла, но в других частях файла все еще используется оператор print. В Python 2.6-2.7 вы можете получить функцию Python 3 print с помощью:

import __builtin__

print3 = __builtin__.__dict__['print']

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * from __future__ import print_function * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1066* * * * * * * * '' '' * ”] *1066* На самом деле *1066* фактически не импортирует функцию print в любом месте Python 2 - вместо этого он просто отключает правила синтаксического анализа для оператора print в текущем модуле, обрабатывая print, как любой другой идентификатор переменной, и, таким образом, разрешение функции print для поиска во встроенных функциях.

21 голосов
/ 15 ноября 2008

Правила области видимости для Python 2.x уже были изложены в других ответах. Единственное, что я хотел бы добавить, это то, что в Python 3.0 также существует концепция нелокальной области (обозначается ключевым словом «nonlocal»). Это позволяет вам получить прямой доступ к внешним областям и открывает возможность делать некоторые изящные трюки, включая лексические замыкания (без уродливых хаков, связанных с изменяемыми объектами).

РЕДАКТИРОВАТЬ: Вот PEP с дополнительной информацией об этом.

20 голосов
/ 04 декабря 2015

Немного более полный пример объема:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

выход:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200
11 голосов
/ 15 ноября 2008

Python разрешает ваши переменные с - обычно - тремя доступными пространствами имен.

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

Существует две функции: globals и locals, которые показывают содержимое двух этих пространств имен.

Пространства имен создаются пакетами, модулями, классами, конструкциями объектов и функциями. Других разновидностей пространств имен нет.

В этом случае вызов функции с именем x должен быть разрешен в локальном пространстве имен или глобальном пространстве имен.

Локальным в данном случае является тело функции метода Foo.spam.

Глобальный - хорошо - глобальный.

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

Других областей нет. Оператор for (и другие составные операторы, такие как if и try) не создают новые вложенные области. Только определения (пакеты, модули, функции, классы и экземпляры объектов.)

Внутри определения класса имена являются частью пространства имен класса. code2, например, должно быть квалифицировано по имени класса. Обычно Foo.code2. Однако self.code2 также будет работать, потому что объекты Python рассматривают содержащий класс как запасной вариант.

Объект (экземпляр класса) имеет переменные экземпляра. Эти имена находятся в пространстве имен объекта. Они должны быть квалифицированы по объекту. (variable.instance.)

Из метода класса у вас есть локальные и глобальные переменные. Вы говорите self.variable, чтобы выбрать экземпляр в качестве пространства имен. Вы заметите, что self является аргументом для каждой функции-члена класса, что делает ее частью локального пространства имен.

См. Правила области Python , Область Python , Область переменных .

7 голосов
/ 15 ноября 2008

Где х найден?

x не найден, поскольку вы его не определили. :-) Его можно найти в code1 (глобальном) или code3 (локальном), если вы поместите его туда.

code2 (члены класса) не видны для кода внутри методов одного и того же класса - вы обычно получаете к ним доступ, используя self. code4 / code5 (циклы) находятся в той же области видимости, что и code3, поэтому, если вы записали туда x, вы бы изменили экземпляр x, определенный в code3, а не создавали новый x.

Python имеет статическую область видимости, поэтому, если вы передадите «spam» другой функции, spam будет по-прежнему иметь доступ к глобальным переменным в модуле, из которого он был создан (определен в code1), и ко всем другим областям, содержащим области (см. Ниже). Члены code2 снова будут доступны через себя.

лямбда ничем не отличается от def. Если в функции используется лямбда-выражение, это то же самое, что и определение вложенной функции. Начиная с версии Python 2.2 доступны вложенные области видимости. В этом случае вы можете связать x на любом уровне вложенности функций, и Python подберет самый внутренний экземпляр:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 видит экземпляр x из ближайшей содержащей области, которая является областью функции, связанной с fun2. Но другие x-экземпляры, определенные в fun1 и глобально, не затрагиваются.

До nested_scopes - в Python pre-2.1 и в 2.1, если только вы не запрашиваете особенность, использующую импорт из будущего - области действия fun1 и fun2 не видны для fun3, поэтому ответ S.Lott верен, и вы получите глобальный х:

0 0
0 голосов
/ 13 мая 2019

В Python,

любая переменная, которой присвоено значение, является локальной для блока, в котором назначение появляется.

Если переменная не может быть найдена в текущей области, пожалуйста, обратитесь к порядку LEGB.

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