глюки побочного эффекта в python/numpy? страшные истории и избавления лишь по счастливой случайности требуются

Я рассматриваю перемещение от 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], [], [], []]

заранее спасибо

7
задан Jon Seigel 16 April 2010 в 18:50
поделиться

1 ответ

Путаница в ссылках на один и тот же (изменяемый) объект со ссылками на отдельные объекты действительно является "ошибкой" (страдает от всех нефункциональных языков, в которых есть изменяемые объекты и, конечно, , Рекомендации). Часто встречающаяся ошибка в коде 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 является аргументом со значением по умолчанию, а не свободной переменной (ищется с помощью лексического замыкания), поэтому код работает так, как задумано (конечно, есть и другие способы ).

9
ответ дан 7 December 2019 в 03:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: