Я рассматриваю перемещение от Matlab до Python/numpy для анализа данных и численного моделирования. Я использовал Matlab (и SML-NJ) в течение многих лет, и очень удобно в функциональной среде без побочных эффектов (запрещающий ввод-вывод), но немного отказываюсь о побочных эффектах в Python. Люди могут совместно использовать свои любимые глюки относительно побочных эффектов, и, если возможно, как они обошли их? Как пример, я был немного удивлен, когда я попробовал следующий код в Python:
lofls = [[]] * 4 #an accident waiting to happen!
lofls[0].append(7) #not what I was expecting...
print lofls #gives [[7], [7], [7], [7]]
#instead, I should have done this (I think)
lofls = [[] for x in range(4)]
lofls[0].append(7) #only appends to the first list
print lofls #gives [[7], [], [], []]
заранее спасибо
Путаница в ссылках на один и тот же (изменяемый) объект со ссылками на отдельные объекты действительно является "ошибкой" (страдает от всех нефункциональных языков, в которых есть изменяемые объекты и, конечно, , Рекомендации). Часто встречающаяся ошибка в коде Python для начинающих - неправильное использование значения по умолчанию, которое является изменяемым, например:
def addone(item, alist=[]):
alist.append(item)
return alist
Этот код может быть правильным, если цель состоит в том, чтобы addone
сохранял свое собственное состояние (и возвращал его. растущий список для последовательных вызывающих), так же, как статические
данные будут работать в C; это неправильно, если кодировщик ошибочно предполагает, что новый пустой список будет создаваться при каждом вызове.
Неопытных новичков, привыкших к функциональным языкам, также может сбить с толку разделение команд и запросов проектное решение во встроенных контейнерах Python: изменяющиеся методы, которые не имеют ничего конкретного для возврата (т. Е., подавляющее большинство методов изменения) ничего не возвращают (в частности, они возвращают None
) - они выполняют всю свою работу «на месте». Ошибки, возникающие из-за неправильного понимания этого, легко обнаружить, например
alist = alist.append(item)
практически гарантированно является ошибкой - он добавляет элемент в список, на который ссылается имя alist
, но затем повторно связывает имя alist
в None
] (возвращаемое значение вызова append
).
В то время как первая проблема, о которой я упомянул, касается раннего связывания, которое может ввести в заблуждение людей, которые думают, что связывание является поздним, есть проблемы, которые идут другим путем, когда некоторые люди ожидают раннего связывания, в то время как связывание происходит с опозданием. Например (с гипотетической структурой графического интерфейса ...):
for i in range(10):
Button(text="Button #%s" % i,
click=lambda: say("I'm #%s!" % i))
это покажет десять кнопок с надписью «Кнопка №0», «Кнопка №1» и т. Д., Но при нажатии каждая из них будет скажем
это # 9
- потому что i
в лямбда
привязано поздно (с лексическим закрытием). Исправление состоит в том, чтобы воспользоваться преимуществом того факта, что значения по умолчанию для аргумента имеют раннюю привязку (как я уже говорил о первой проблеме! -), и изменить последнюю строку на
click=lambda i=i: say("I'm #%s!" % i))
Теперь лямбда
i
является аргументом со значением по умолчанию, а не свободной переменной (ищется с помощью лексического замыкания), поэтому код работает так, как задумано (конечно, есть и другие способы ).