python/numpy的副作用陷阱?恐怖故事和侥幸逃脱

2024-09-30 01:36:32 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在考虑从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], [], [], []]

提前谢谢


Tags: to功能numpy环境数值副作用printmatlab
2条回答

我最近在尝试消除对numpy的一个小依赖时,又偶然发现了这个问题(在python多年之后)。在

如果您来自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

等等

将对同一个(可变)对象的引用与对不同对象的引用混淆起来确实是一个“问题”(所有非函数性语言都会遇到这种情况,即具有可变对象和引用的语言)。初学者Python代码中经常出现的一个错误是误用了一个可变的默认值,例如:

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

如果目的是让addone保持自己的状态(并将一个增长的列表返回给连续的调用方),则此代码可能是正确的,就像static数据在C中可以工作一样;如果编码人员错误地假设在每次调用时都会生成一个新的空列表,那么这段代码就不正确了。在

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

^{pr2}$

几乎可以肯定是一个bug,它将一个条目附加到名为alist的列表中,然后将name alist重新绑定到None(调用append的返回值)。在

虽然我提到的第一个问题是关于早期绑定,可能会误导那些认为绑定是延迟绑定的人,但是也有一些问题是相反的,即有些人的期望是早期绑定,而绑定则是延迟绑定。例如(使用假设的GUI框架…):

for i in range(10):
    Button(text="Button #%s" % i,
           click=lambda: say("I'm #%s!" % i))

这将显示10个按钮,表示“Button#0”、“Button#1”等,但是,当单击时,每个按钮都将say,它是{},因为{}中的i是晚绑定的(带有词法闭包)。一个修复方法是利用参数的默认值是早期绑定的这一事实(正如我在第一个问题中指出的那样!-)最后一行改成

           click=lambda i=i: say("I'm #%s!" % i))

现在lambdai是一个带有默认值的参数,不再是一个自由变量(通过词法闭包查找),因此代码按预期工作(当然还有其他方法)。在

相关问题 更多 >

    热门问题