副作用陷入python / numpy?想要恐怖故事和狭隘逃脱

时间:2010-03-09 18:19:54

标签: python matlab functional-programming side-effects

我正在考虑从Matlab转到Python / numpy进行数据分析和数值模拟。我已经使用Matlab(和SML-NJ)多年了,并且在功能环境中非常舒适而没有副作用(除了I / O),但我对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 个答案:

答案 0 :(得分:9)

混淆对相同(可变)对象的引用与对单独对象的引用确实是一个“陷阱”(所有非功能语言都遭受了攻击,具有可变对象的语言,当然还有引用)。初学者Python代码中经常出现的错误是滥用了一个可变的默认值,例如:

def addone(item, alist=[]):
  alist.append(item)
  return alist

如果目的是使addone保持自己的状态(并将一个增长列表返回给连续的调用者),则此代码可能是正确的,就像static数据在C中工作一样;如果编码器错误地认为每次通话都会产生一个新的空列表,那就不正确了。

用于函数式语言的原始初学者也可能被Python内置容器中的command-query separation设计决策所混淆:变异方法没有特别要返回的东西(即绝大多数变异方法)什么都不返回(具体来说,他们返回None) - 他们“就地”完成所有工作。来自误解的错误很容易被发现,例如。

alist = alist.append(item)

几乎可以保证是一个错误 - 它将一个项目附加到名称alist引用的列表中,然后将名称alist重新绑定到None(返回值为append来电。{/ p>

虽然我提到的第一个问题是关于早期约束可能会误导那些认为绑定是迟到的人,但是有一些问题是另一种方式,其中一些人的期望是早期绑定相反,绑定是迟到的。例如(使用假设的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的{​​{1}}是一个带有默认值的参数,而不是一个自由变量(由词法闭包查找),因此代码按预期工作(还有其他方法)当然也是。)

答案 1 :(得分:0)

我最近再次偶然发现了这个(经过多年的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