Информация о состоянии и неизменность списков в Python - PullRequest
0 голосов
/ 02 июня 2018

Я новичок в Python и использую книгу Марка Лутца, чтобы изучить основы Python.

Вот пример, который автор использует для демонстрации хранения информации о состоянии с использованием списков:

def tester(start):
    def nested(label):
        print(label,state[0])
        state[0] += 1
    state = [start]
    return nested

Вот код для проверки информации о состоянии:

F = tester(3)
F('sam')
F('sam')

Вы увидитечто счетчик увеличивается с 3, а затем продолжается.По сути, вышеприведенный код сохраняет начальное состояние start (переданное во время инициализации объекта) в [state] и увеличивает его каждый раз, когда вызывается label.

Однако я не уверен, почему Python не выдает ошибку в блоке nested.В частности, [state] является локальным по отношению к tester, а не nested.

Чтобы продемонстрировать, что я имею в виду, я собираюсь заменить state[0] на state.

def tester(start):
    def nested(label):
        print(label,state) #Replaced state[0] with state
        state += 1         #Replaced state[0] with state
        print("after:",state)
    state = start          #Replaced state[0] with state
    return nested

ТехническиВышеприведенный код также должен работать нормально, потому что все, что я сделал, это заменил список переменной.Однако PyCharm даже не запускает этот код.Я получаю сообщение об ошибке nboundLocalError: local variable 'state' referenced before assignment

Может кто-нибудь объяснить, почему версия с list работает нормально?Автор заявил, что «это использует изменчивость списков и основывается на том факте, что объект на месте не классифицирует имя как локальное».

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

Ответы [ 3 ]

0 голосов
/ 02 июня 2018

Насколько я понимаю, поскольку nested вложено в tester, оно будет иметь доступ к любым объектам и переменным, которые принадлежат tester, поскольку tester является родительской функцией, а nested является дочерней функцией.в этом случае.Python не выдаст ошибку из-за наследования .

. А что касается замены state[0] на state, Python автоматически предполагает, что state является integer, потому что вы пытаетесьдобавить к этому.Хотя state является списком, и вы не можете добавить его, если только не добавит элемент к нему, что не является вашим случаем.Причина, по которой state[0] работает, а не state, заключается в том, что state[0] является элементом в списке state и добавляет к нему 0.

0 голосов
/ 02 июня 2018

Это функция 1) того, как присвоение переменных Python фактически просто создает псевдонимы (указатели) на базовые значения в памяти, и разницу между тем, как обрабатываются изменяемые и неизменяемые типы;и 2) некоторая «магия» Python, связанная с замыканиями.Суть вашего вопроса - действительно первый пункт.

Чтобы решить эту проблему, возьмите, например, следующее:

a = 3
b = 3

И a, и b указывают на один и тот же базовый объект:

assert hex(id(a)) == hex(id(b)) 

- True.Однако затем установка b = 4 приведет к тому, что b будет указывать на другой объект в памяти (показывая, что int является неизменяемым).

Однако список является изменяемым (изменяемым «на месте»).Например: c = [2] будет иметь одну и ту же ячейку памяти до и после такой операции, как c[0] = 3.

. Это очень простое объяснение имеет множество последствий, для интерпретации которых требуется некоторое время.Например, переменные не могут «указывать» на другие переменные, но все же указывают на лежащие в основе объекты.

В результате списки могут демонстрировать «странное» поведение (еще одна распространенная путаница связана с установкой по умолчанию).значение параметра в виде списка, который затем изменяется в функции), но его также можно использовать, как показано в примере.

0 голосов
/ 02 июня 2018

Вы должны прочитать этот раздел документации .

По сути, в обеих версиях область вложенного блока позволяет ему взаимодействовать с пространством имен охватывающего блока.Разница в том, что вы не переназначаете state в первом примере, вы изменяете его.

Во втором примере Python знает, что вы собираетесь присвоить значение этой ссылке позже в функции, поэтому оно обрабатывается как имя из локального пространства имен nested, а не из внешнего tester namespace.

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

def tester(start):
    def nested(label):
        nonlocal state
        print(label,state) 
        state += 1         
        print("after:",state)
    state = start          
    return nested
...