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

Каковы точные правила Python?

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

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

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

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

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

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

7 ответов

Решение

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

Правило 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)?

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

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

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

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

Как указано в других ответах, есть 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) # -> 5
    x += 1

foobar()
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()      # -> print 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 на самом деле не импортирует print функция в любом месте Python 2 - вместо этого он просто отключает правила синтаксического анализа для print оператор в текущем модуле, обработка print как и любой другой идентификатор переменной, и, таким образом, позволяя print функция должна быть найдена во встроенных.

Немного более полный пример области применения:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Где х найден?

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

Разрешение имен Python знает только следующие виды области:

  1. встроенная область, которая предоставляет встроенные функции, такие какprint, int, или zip,
  2. глобальная область видимости модуля, которая всегда является верхним уровнем текущего модуля,
  3. три пользовательских области видимости, которые могут быть вложены друг в друга, а именно
    1. область закрытия функции, из любого включающегоdef блок lambda выражение или понимание.
    2. локальная область видимости функции внутриdef блок lambda выражение или понимание,
    3. область видимости класса внутриclass блок.

В частности, другие конструкции, такие как if, for, или with утверждения не имеют собственной области действия.

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

Наконец, понимания и выражения генератора, а также := При объединении выражений присваивания применяется одно специальное правило.


Вложенные области и разрешение имен

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

print("builtins are available without definition")

some_global = "1"  # global variables are at module scope

def outer_function():
    some_closure = "3.1"  # locals and closure are defined the same, at function scope
    some_local = "3.2"    # a variable becomes a closure if a nested scope uses it

    class InnerClass:
         some_classvar = "3.3"   # class variables exist *only* at class scope

         def nested_function(self):
             some_local = "3.2"   # locals can replace outer names
             print(some_closure)  # closures are always readable
    return InnerClass

Даже если class создает область видимости и может иметь вложенные классы, функции и понимания, имена classобласти не видны закрытым областям. Это создает следующую иерархию:

┎ builtins           [print, ...]
┗━┱ globals            [some_global]
  ┗━┱ outer_function     [some_local, some_closure]
    ┣━╾ InnerClass         [some_classvar]
    ┗━╾ inner_function     [some_local]

Разрешение имен всегда начинается с текущей области, в которой осуществляется доступ к имени, затем идет вверх по иерархии, пока не будет найдено совпадение. Например, глядя вверхsome_local внутри outer_function а также inner_function начинается с соответствующей функции - и сразу находит some_local определено в outer_function а также inner_functionсоответственно. Когда имя не является локальным, оно извлекается из ближайшей охватывающей области, которая его определяет - поискsome_closure а также print внутри inner_function ищет до outer_function и встроенные соответственно.


Объявления области действия и привязка имени

По умолчанию имя принадлежит любой области, в которой оно привязано к значению. Повторное связывание того же имени во внутренней области видимости создает новую переменную с тем же именем - например,some_local существует отдельно в обоих outer_function а также inner_function. Что касается области видимости, привязка включает любой оператор, который устанавливает значение оператора присваивания имени, а также переменную итерацииfor цикл, или имя withменеджер контекста. В частности,del также считается привязкой имени.

Когда имя должно ссылаться на внешнюю переменную и быть привязано во внутренней области, имя должно быть объявлено как не локальное. Существуют отдельные объявления для различных типов охватывающих областей:nonlocal всегда относится к ближайшему закрытию, и globalвсегда относится к глобальному имени. В частности,nonlocal никогда не ссылается на глобальное имя и globalигнорирует все закрытия с тем же именем. Нет объявления для ссылки на встроенную область видимости.


some_global = "1"

def outer_function():
    some_closure = "3.2"
    some_global = "this is ignored by a nested global declaration"
    
    def inner_function():
        global some_global     # declare variable from global scope
        nonlocal some_closure  # declare variable from enclosing scope
        message = " bound by an inner scope"
        some_global = some_global + message
        some_closure = some_closure + message
    return inner_function

Следует отметить, что функция local и nonlocalразрешаются во время компиляции. Аnonlocalимя должно существовать во внешней области видимости. Напротив,global name может быть определено динамически и может быть добавлено или удалено из глобальной области в любое время.


Понимания и выражения присваивания

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

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

some_global = "global"

def outer_function():
    some_closure = "closure"
    return [            # new function-like scope started by comprehension
        comp_local      # names resolved using regular name resolution
        for comp_local  # iteration targets are local
        in "iterable"
        if comp_local in some_global and comp_local in some_global
    ]

An :=Выражение присваивания работает с ближайшей функцией, классом или глобальной областью видимости. В частности, если цель выражения присваивания была объявленаnonlocal или global в ближайшей области выражение присваивания учитывает это как обычное присваивание.

print(some_global := "global")

def outer_function():
    print(some_closure := "closure")

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

print(some_global := "global")

def outer_function():
    print(some_closure := "closure")
    steps = [
        # v write to variable in containing scope
        (some_closure := some_closure + comp_local)
        #                 ^ read from variable in containing scope
        for comp_local in some_global
    ]
    return some_closure, steps

Хотя переменная итерации является локальной для понимания, в котором она связана, цель выражения присваивания не создает локальную переменную и считывается из внешней области:

┎ builtins           [print, ...]
┗━┱ globals            [some_global]
  ┗━┱ outer_function     [some_closure]
    ┗━╾ <listcomp>         [comp_local]

В Python

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

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

Другие вопросы по тегам