Вопрос-Ответ

Short description of the scoping rules

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

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

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

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

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


  1. Во вложенном исходном файле

  2. В пространстве имен class

  3. В определении функции

  4. В переменной индекса цикла for

  5. Внутри цикла for

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

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

Переведено автоматически
Ответ 1

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

Правило LEGB


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


  • Enclosing-имена функций, присваиваемые в локальной области видимости всем статически охватываемым функциям (def или lambda), от внутренних до внешних


  • Global (module) — Имена, присваиваемые на верхнем уровне файла модуля или путем выполнения global инструкции в def внутри файла


  • Built-in (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 (даже в тех случаях, когда вы могли бы ожидать этого, смотрите Ответ Антти или здесь).

Ответ 2

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

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


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


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


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

Ответ 3

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

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

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

To demonstrate the peculiarities of the class body

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)

Thus unlike in function body, you can reassign the variable to the same name in class body, to get a class variable with the same name; further lookups on this name resolve
to the class variable instead.


One of the greater surprises to many newcomers to Python is that a for loop does not create a variable scope. In Python 2 the list comprehensions do not create a scope either (while generators and dict comprehensions do!) Instead they leak the value in the function or the global scope:

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

The comprehensions can be used as a cunning (or awful if you will) way to make modifiable variables within lambda expressions in Python 2 - a lambda expression does create a variable scope, like the def statement would, but within lambda no statements are allowed. Assignment being a statement in Python means that no variable assignments in lambda are allowed, but a list comprehension is an expression...

This behaviour has been fixed in Python 3 - no comprehension expressions or generators leak variables.


The global really means the module scope; the main python module is the __main__; all imported modules are accessible through the sys.modules variable; to get access to __main__ one can use sys.modules['__main__'], or import __main__; it is perfectly acceptable to access and assign attributes there; they will show up as variables in the global scope of the main module.


If a name is ever assigned to in the current scope (except in the class scope), it will be considered belonging to that scope, otherwise it will be considered to belonging to any enclosing scope that assigns to the variable (it might not be assigned yet, or not at all), or finally the global scope. If the variable is considered local, but it is not set yet, or has been deleted, reading the variable value will result in UnboundLocalError, which is a subclass of 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()

The scope can declare that it explicitly wants to modify the global (module scope) variable, with the global keyword:

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

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

This also is possible even if it was shadowed in enclosing scope:

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

In python 2 there is no easy way to modify the value in the enclosing scope; usually this is simulated by having a mutable value, such as a list with length of 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

However in python 3, the nonlocal comes to rescue:

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.

The nonlocal documentation says that


Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).


i.e. nonlocal always refers to the innermost outer non-global scope where the name has been bound (i.e. assigned to, including used as the for target variable, in the with clause, or as a function parameter).


Any variable that is not deemed to be local to the current scope, or any enclosing scope, is a global variable. A global name is looked up in the module global dictionary; if not found, the global is then looked up from the builtins module; the name of the module was changed from python 2 to python 3; in python 2 it was __builtin__ and in python 3 it is now called builtins. If you assign to an attribute of builtins module, it will be visible thereafter to any module as a readable global variable, unless that module shadows them with its own global variable with the same name.


Reading the builtin module can also be useful; suppose that you want the python 3 style print function in some parts of file, but other parts of file still use the print statement. In Python 2.6-2.7 you can get hold of the Python 3 print function with:

import __builtin__

print3 = __builtin__.__dict__['print']

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

Ответ 4

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

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