Почему оператор Pythons + = (plus equals) не изменяет переменные из внутренних функций? - PullRequest
5 голосов
/ 23 июля 2011

Я хотел бы узнать подробности о том, почему это не работает должным образом:

def outer():
    mylist = []
    def inner():
        mylist += [1]

    inner()

outer()

Тем более, что mylist.__iadd__([1]) отлично работает.

Ответы [ 4 ]

10 голосов
/ 23 июля 2011

Проблема в том, что когда вы присваиваете имя переменной внутри функции, Python предполагает, что вы пытаетесь создать новую локальную переменную, которая будет маскировать переменную с аналогичным именем во внешней области видимости.Поскольку += должен получить значение mylist, прежде чем он сможет его изменить, он жалуется, потому что локальная версия mylist еще не определена. Ответ MRAB дает четкое объяснение семантики.

С другой стороны, когда вы делаете mylist.__iadd__([1]), вы не назначаете имя новой переменной внутри функции.Вы просто используете встроенный метод для изменения уже назначенного имени переменной.Пока вы не попытаетесь присвоить новое значение mylist, у вас не будет проблем.По той же причине строка mylist[0] = 5 также будет работать внутри inner, если бы определение mylist в outer было mylist = [1].

Обратите внимание, однако, что если вы попытаетесь присвоить новое значение для mylist в любом месте функции, mylist.__iadd__([1]) действительно потерпит неудачу:

>>> outer()
>>> def outer():
...     mylist = []
...     def inner():
...         mylist.__iadd__([1])
...         mylist = []
...     inner()
... 
>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in outer
  File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'mylist' referenced before assignment

Если вы хотите назначить новоезначение переменной из содержащей области, в 3.0+ вы можете использовать nonlocal, точно так же, как вы используете global, чтобы назначить новое значение переменной в глобальной области.Поэтому вместо этого:

>>> mylist = []
>>> def inner():
...     global mylist
...     mylist += [1]
... 
>>> inner()
>>> mylist
[1]

Вы делаете это:

def outer():
    mylist = []
    def inner():
        nonlocal mylist
        mylist += [1]
    inner()
    print(mylist)
outer()
5 голосов
/ 23 июля 2011

Если имя связано (назначена переменная) в функции, то это имя считается локальным, если оно не объявлено глобальным.

Таким образом, в inner, mylist является локальным.

Когда вы пишете x += y, во время выполнения Python попытается:

x = x.__iadd__(y)

Если это не удается, Python пытается:

x = x.__add__(y)
1 голос
/ 23 июля 2011

Это потому, что myList внутри inner() не относится к myList, определенному в outer(), и поэтому оператор равенства равно не работает. На мой взгляд, есть два решения.

Во-первых, в качестве аргумента будет передаваться myList во внутреннее:

def outer():
    mylist = []
    def inner(someList):
        somelist += [1]

    inner(mylist)

outer()

Второе решение - объявить myList вне обеих функций, а затем объявить его как global внутри обеих функций:

mylist = []
def outer():
    global mylist
    mylist = []
    def inner():
        global mylist
        mylist += [1]

    inner()

outer()

Хотя я бы порекомендовал первое решение.

1 голос
/ 23 июля 2011

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

Чтобы назначить глобальную переменную, вы делаете:

global var
var = 5

Вы не можете делать то, что делаетев Python 2, но в Python 3 вы можете сделать:

nonlocal mylist
mylist += [1]

Альтернатива Python 2 для изменения элемента или атрибута чего-либо:

def outer():
    mylist = []
    def inner(mylist = mylist):
        mylist += [1]
    inner()
outer()

Если вы хотите заменить значениепеременной необходимо:

def outer():
    def setlist(newlist):
        mylist = newlist
    mylist = []
    def inner():
        setlist(['new_list'])
    inner()
outer()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...