Подтвердить что ты не робот

Почему функции в Python могут печатать переменные в приложении, но не могут использовать их при назначении?

Если я запустил следующий код:

x = 1

class Incr:
    print(x)
    x = x + 1
    print(x)

print(x)

Он печатает:

1
2
1

Ладно, никаких проблем, это то, чего я ожидал. И если я сделаю следующее:

x = 1

class Incr:
    global x
    print(x)
    x = x + 1
    print(x)

print(x)

Он печатает:

1
2
2

И что я ожидал. Проблем нет.

Теперь, если я начну делать функцию инкремента следующим образом:

x = 1

def incr():
    print(x)

incr()

Он печатает 1 так, как я ожидал. Я предполагаю, что он делает это, потому что он не может найти x в своей локальной области, поэтому он ищет свою охватывающую область и находит x там. Пока никаких проблем.

Теперь, если я делаю:

x = 1

def incr():
    print(x)
    x = x + 1

incr()

Это дает мне следующую ошибку в трассировке:

UnboundLocalError: локальная переменная 'x', указанная перед назначением.

Почему Python не просто ищет закрывающее пространство для x, когда он не может найти значение x для использования для назначения, как это сделал мой class Incr? Обратите внимание, что я не спрашиваю, как заставить эту функцию работать. Я знаю, что функция будет работать, если я сделаю следующее:

x = 1

def incr():
    global x
    print(x)
    x = x + 1
    print(x)

incr()

Это правильно напечатает:

1
2

так, как я ожидаю. Все, что я прошу, это то, что он не просто тянет x из охватывающей области, когда ключевое слово global отсутствует, как это было для моего класса выше. Почему интерпретатор чувствует необходимость сообщать об этом как UnboundLocalError, когда он ясно знает, что существует некоторый x. Поскольку функция была в состоянии читать значение x для печати, я знаю, что она имеет x как часть своей охватывающей области... поэтому почему это не работает так же, как пример класса?

Почему используется значение x для печати, отличное от использования его значения для назначения? Я просто не понимаю.

4b9b3361

Ответ 1

Классы и функции разные, переменные внутри класса фактически фактически назначаются пространству имен классов в качестве его атрибутов, а внутри функции переменные являются только нормальными переменными, к которым нельзя получить доступ за пределами.

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

Итак, как только python увидит x = x + 1 (присвоение), и там нет global, объявленного для этой переменной, тогда python не будет искать эту переменную в глобальных или других областях.

>>> x = 'outer'
>>> def func():
...     x = 'inner'  #x is a local variable now
...     print x
...     
>>> func()
inner

Общий доступ:

>>> x = 'outer'
>>> def func():
...     print x       #this won't access the global `x`
...     x = 'inner'   #`x` is a local variable
...     print x
...     
>>> func()
...
UnboundLocalError: local variable 'x' referenced before assignment

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

Читайте: Почему я получаю UnboundLocalError, когда переменная имеет значение?

nonlocal. Для вложенных функций вы можете использовать оператор nonlocal в py3.x для изменения переменной, объявленной в закрывающей функции.


Но классы работают по-другому, переменная x, объявленная внутри класса A, фактически становится A.x:

>>> x = 'outer'
>>> class A:
...    x += 'inside'  #use the value of global `x` to create a new attribute `A.x`
...    print x        #prints `A.x`
...     
outerinside
>>> print x
outer

Вы также можете получить доступ к атрибутам класса непосредственно из глобальной области:

>>> A.x
'outerinside'

Использование global в классе:

>>> x = 'outer'
>>> class A:
...     global x
...     x += 'inner' #now x is not a class attribute, you just modified the global x
...     print x
...     
outerinner
>>> x
'outerinner'
>>> A.x
AttributeError: class A has no attribute 'x'

Функция gotcha не вызывает ошибки в классах:

>>> x = 'outer'
>>> class A:
...     print x                      #fetch from globals or builitns
...     x = 'I am a class attribute' #declare a class attribute
...     print x                      #print class attribute, i.e `A.x`
...     
outer
I am a class attribute
>>> x
'outer'
>>> A.x
'I am a class attribute'

LEGB: если нет global и nonlocal, тогда поиск python выполняется в этом порядке.

>>> outer = 'global'
>>> def func():
        enclosing = 'enclosing'
        def inner():
                inner = 'inner'
                print inner           #fetch from (L)ocal scope
                print enclosing       #fetch from (E)nclosing scope
                print outer           #fetch from (G)lobal scope
                print any             #fetch from (B)uilt-ins
        inner()
...         
>>> func()
inner
enclosing
global
<built-in function any>

Ответ 2

Из Python области и пространства имен:

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

Это означает, что область x = x + 1 определяется статически, перед вызовом функции. И поскольку это назначение, то "x" становится локальной переменной и не отображается глобально.

Это также причина, по которой from mod import * запрещена в функциях. Поскольку интерпретатор не будет импортировать модули для вас во время компиляции, чтобы узнать имена, которые вы используете в этой функции. то есть он должен знать все имена, на которые ссылается функция во время компиляции.

Ответ 3

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

О:

Я предполагаю, что он делает это, потому что не может найти x в своей локальной области, поэтому он ищет свою закрывающую область и находит x.

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

Почему вам не нужно объявлять имя global только для ссылки на его значение, мне нравится Fredrik Lundh старый ответ здесь, На практике действительно полезно, что оператор global предупреждает читателей кода, что функция может переписывать глобальное имя.

Ответ 4

Потому что он был разработан для работы.

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

Ответ 5

Потому что это приведет к очень трудному отслеживанию ошибок!

Когда вы набираете x = x + 1, вы могли бы увеличить значение x в охватывающей области... или вы могли просто забыть, что вы уже использовали x где-то еще и пытались объявить локальную переменную.

Я бы предпочел, чтобы интерпретатор разрешил вам изменять родительское пространство имен, если вы намереваетесь - таким образом вы не можете сделать это случайно.

Ответ 6

Я могу попытаться сделать обоснованное предположение, почему он работает таким образом.

Когда Python встречает строку x = x + 1 в вашей функции, она должна решить, где искать x.

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

Существует задание, поэтому если x должен был быть глобальным, был бы оператор global, но ни один не найден.

Следовательно, x является локальным, но он ничем не связан, и все же он используется в выражении x + 1. Бросьте UnboundLocalError.

Ответ 7

В качестве дополнительного примера для вновь созданного A.x, созданного в классе. Перераспределение x в 'inner' внутри класса не обновляет глобальное значение x, потому что теперь это переменная класса.

x = 'outer'
class A:
    x = x
    print(x)
    x = 'inner'
    print(x)

print(x)
print(A.x)