Смешение ссылок на один и тот же (изменяемый) объект со ссылками на отдельные объекты действительно является «уловкой» (от которой страдают все нефункциональные языки, те, которые имеют изменяемые объекты и, конечно же, ссылки). Часто встречающаяся ошибка в коде Python для начинающих - неправильное использование значения по умолчанию, которое является изменяемым, например ::
def addone(item, alist=[]):
alist.append(item)
return alist
Этот код может быть правильным, если цель состоит в том, чтобы addone
сохранял свое собственное состояние (и возвращал один растущий список последующим вызывающим абонентам), почти как данные static
работали бы в C; неверно, если кодировщик ошибочно полагает, что при каждом вызове будет создаваться новый пустой список.
Необработанные новички, используемые в функциональных языках, также могут быть сбиты с толку разделением команды-запроса дизайнерским решением во встроенных контейнерах Python: методы мутации, которые не имеют ничего конкретного для возврата (т. Е. Подавляющее большинство методов мутации) ничего не возвращают (в частности, они возвращают None
) - они делают всю свою работу "на месте". Ошибки, возникающие из-за неправильного понимания этого, легко обнаружить, например,
alist = alist.append(item)
гарантированно является ошибкой - он добавляет элемент в список, на который ссылается имя alist
, но затем связывает имя alist
с None
(возвращаемое значение вызова append
) .
Хотя первая проблема, о которой я упоминал, касается раннего связывания, которое может ввести в заблуждение людей, которые считают, что связывание является поздним, существуют проблемы, которые идут другим путем, когда некоторые люди ожидают раннего связывания, в то время как переплет, вместо этого, поздно. Например (с гипотетической структурой GUI ...):
for i in range(10):
Button(text="Button #%s" % i,
click=lambda: say("I'm #%s!" % i))
это покажет десять кнопок с надписью «Кнопка # 0», «Кнопка # 1» и т. Д., Но при нажатии каждая из них будет say
это #9
- потому что i
в пределах lambda
имеет позднюю привязку (с лексическим закрытием). Исправление заключается в том, чтобы воспользоваться тем фактом, что значения по умолчанию для аргумента имеют раннее связывание (как я уже говорил о первой проблеме! -) и изменить последнюю строку на
click=lambda i=i: say("I'm #%s!" % i))
Теперь lambda
* i
- это аргумент со значением по умолчанию, а не свободная переменная (ищется по лексическому замыканию), и поэтому код работает как задумано (конечно, есть и другие способы) ).