Краткое описание правил определения области видимости
Что именно представляют собой правила определения области видимости Python?
Если у меня есть какой-то код:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
Где находится x
? Некоторые возможные варианты включают приведенный ниже список:
- Во вложенном исходном файле
- В пространстве имен class
- В определении функции
- В переменной индекса цикла for
- Внутри цикла 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 spam
(вcode3
,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