Краткое описание правил определения объема?

472

Что именно правила обзорных Python?

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

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

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

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

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

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

Чарльз Мерриам
источник
2
Правила области видимости описаны довольно кратко - но также полностью - в документации Python: docs.python.org/3/reference/… .
Jefe2000

Ответы:

420

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

Правило LEGB

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

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

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

  • B uilt в (Python) - Имена предустанавливаются в встроенном модуле имен: open, range, SyntaxErrorи т.д.

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

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

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

  • L: местный в def spamcode3, code4и code5)
  • E: Любые включающие функции (если весь пример был в другом def)
  • G: Были ли какие-либо xобъявлены глобально в модуле (в code1)?
  • B: Любой встроенный xв Python.

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

Ризван Кассим
источник
45
В качестве пояснения к глобальному доступу - чтение глобальной переменной может происходить без явного объявления, но запись в нее без объявления глобального (var_name) вместо этого создаст новый локальный экземпляр.
Питер Гибсон
12
На самом деле @Peter, global(var_name)синтаксически неверен. Правильный синтаксис был бы global var_nameбез скобок. У вас есть правильное мнение, хотя.
Мартино
Если так, то почему переменная "y" из foo не видна "bar" ниже: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Джонатан Майер
3
@Jonathan: Потому что каждый yпишется, а global yобъявлений нет - см. Комментарий Питера.
Мартино
@LakshmanPrasad Он попадает в «E», но имеет одно особое поведение, о котором стоит упомянуть: это переменная класса, поэтому она является «глобальной» среди своих объектов. Присвоение ему будет приводить к непредсказуемому и трудно проблемам отладки , если вы не знаете , что вы делаете.
Ctrl-C
157

По сути, единственное, что в 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

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

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

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", которое будет действовать аналогично глобальному, но для областей вложенных функций.

Брайан
источник
111

Не было никакого подробного ответа относительно времени Python3, поэтому я сделал ответ здесь. Большая часть того, что здесь описано, подробно описана в разделе 4.2.2 Разрешение имен документации Python 3.

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

В частности, нет оператора блока, кроме как 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.

В nonlocalдокументации сказано, что

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

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


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


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

import __builtin__

print3 = __builtin__.__dict__['print']

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

Антти Хаапала
источник
23

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

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

Джереми Кантрелл
источник
23

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

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
brianray
источник
6
Это отличный ответ. Тем не менее, я думаю, что различия между methodи method_local_refдолжны быть выделены. methodможет получить доступ к глобальной переменной и распечатать ее как в 5. Global x. Но method_local_refне может, потому что позже он определяет локальную переменную с тем же именем. Вы можете проверить это, убрав x = 200строку и увидев разницу
Кирилл
@brianray: А как насчет Z?
Малик А. Руми
@kiril Я добавил примечание об этом
brianray
@ MalikA.Rumi Я удалил z, так как это было неинтересно
brianray
Удивительно, но это единственное четкое объяснение областей применения Python, которое я смог найти во всех SO. Просто используя очень простой пример. Спасибо!
not2qubit
13

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

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

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

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

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

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

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

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

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

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

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

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

См Python Scope правила , Python Область , Область видимости переменной .

С. Лотт
источник
5
Это устарело. Начиная с версии 2.1 (7 лет назад) существует более двух областей, поскольку вложенные функции представляют новые области, поэтому функция внутри функции будет иметь доступ к своей локальной области действия, области действия включаемых функций и глобальной области действия (также встроенным).
Брайан
Извините, это больше не так. Python has two namespaces available. Global and local-to-something.
Ризван Кассим
9

Где х найден?

х не найден, так как вы его не определили. :-) Его можно найти в 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 верен, и вы получите глобальный x :

0 0
bobince
источник
1

В Python

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

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

GraceMeng
источник