Какое влияние оказывает изменчивость объекта на область видимости в Python? - PullRequest
1 голос
/ 23 марта 2012

Следующий (абсурдный, но иллюстративный) код работает, как и ожидалось, изменяя список, определенный в функции включения:

def outside1():
    l = list('abcd')
    def inside(a):
        print "Before - %i: %r" % (id(l), l)
        l.append(a)
        print "After - %i: %r\n" % (id(l), l)
    return inside

f = outside1()
[f(c) for c in 'efgh']

Этот код также работает, показывая, что неизменяемый объект, определенный во вложенной области видимости, доступен во вложенной области видимости:

def outside2():
    t = tuple('abcd')
    def inside():
        print "%i: %r" % (id(t), t)
    return inside

outside2()()

Однако это не так с local variable 't' referenced before assignment:

def outside3():
    t = tuple('abcd')
    def inside(a):
        print "Before - %i: %r" % (id(t), t)
        t = t + (a,)
        print "After - %i: %r\n" % (id(t), t)
    return inside

f = outside3()
[f(c) for c in 'efgh']

Может кто-нибудь объяснить, что здесь происходит? Моим первым предположением было то, что я могу видоизмениться, но не назначить для охватывающей области, но я бы по крайней мере ожидал, что оператор печати до сработает, учитывая, что outside2 работает.

Ответы [ 3 ]

6 голосов
/ 23 марта 2012

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

t = t + (a,)

отображает t local для inside(), и любой поиск t внутри inside() попытается найти локальную переменную inside().При превышении указанной выше строки t еще не существует, поэтому возникает ошибка.

В Python 3.x вы можете решить эту проблему, явно объявив t как nonlocal:

def outside3():
    t = tuple('abcd')
    def inside(a):
        nonlocal t
        print("Before - %i: %r" % (id(t), t))
        t = t + (a,)
        print("After - %i: %r\n" % (id(t), t))
    return inside

Все это совершенно не связано с изменчивостью.Ваш пример использования списков не переназначает имя l, в то время как пример использования кортежа делает переназначение t;и это важное отличие, а не изменчивость.

3 голосов
/ 23 марта 2012

Изменчивость не влияет на область действия.

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

1 голос
/ 23 марта 2012

Марчин прав.Изменчивость абсолютно не влияет на область видимости.

Что вам нужно понять, так это то, что в первом примере, когда вы «изменяете список», на который указывает l, вы просто читаете переменную l изатем вызывая некоторый метод (.append()).Это точно так же, как и во втором примере, где вы читаете переменную t.

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

В третьем примере вы присваиваете переменную t.В этом разница.Python 2.x не имеет возможности назначать внешние переменные, кроме глобальных переменных, через global.Python 3.x имеет nonlocal, чтобы позволить вам сделать это.Обратите внимание, что изменчивость не имеет к этому никакого отношения : если вы попытались присвоить (а не просто изменить объект, на который указывает) переменную l в первом примере, выстолкнулся бы с той же проблемой:

def outside1():
    l = list('abcd')
    def inside(a):
        print "Before - %i: %r" % (id(l), l)
        l = [1,2,3]
        print "After - %i: %r\n" % (id(l), l)
    return inside
...