побочный эффект попал в питон / numpy? страшные истории и узкие побеги разыскиваются - PullRequest
7 голосов
/ 09 марта 2010

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

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

Ответы [ 2 ]

9 голосов
/ 09 марта 2010

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

0 голосов
/ 19 марта 2013

Недавно я снова наткнулся на это (после нескольких лет Python), пытаясь убрать небольшую зависимость от numpy.

Если вы пришли из matlab, вы должны использовать и доверять numpy функциям для обработки моно-типа массивов. Наряду с matplotlib они представляют собой несколько очень удобных пакетов для плавного перехода.

import numpy as np
np.zeros((4,)) # to make an array full of zeros [0,0,0,0]
np.zeros((4,1)) # another one full of zeros but 2 dimensions [[0],[0],[0],[0]]
np.zeros((4,0)) # an empty array like [[],[],[],[]]
np.zeros((0,4)) # another empty array, which can not be represented with python lists o_O

и т.д.

...