7

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

提前致谢

4

2 回答 2

9

混淆对同一(可变)对象的引用与对单独对象的引用确实是一个“陷阱”(所有非函数语言,具有可变对象的语言,当然还有引用)。初学者的 Python 代码中一个常见的错误是滥用可变的默认值,例如:

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

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

习惯于函数式语言的初学者也可能会对Python 内置容器中的命令-查询分离设计决策感到困惑:没有任何特别要返回的变异方法(即绝大多数变异方法)不返回任何内容(具体来说,他们返回None)——他们正在“就地”完成所有工作。来自误解的错误很容易发现,例如

alist = alist.append(item)

几乎可以保证是一个错误——它将一个项目附加到由 name 引用的列表中alist,然后将 name 重新绑定alistNoneappend调用的返回值)。

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

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

这将显示十个按钮,上面写着“Button #0”、“Button #1”等,但是,当单击时,它们中的每一个都会say#9- 因为i insidelambda是后期绑定的(带有词汇闭包)。解决方法是利用参数的默认值早期绑定的这一事实(正如我在第一个问题中指出的那样!-)并将最后一行更改为

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

Now lambda'si是具有默认值的参数,不再是自由变量(通过词法闭包查找),因此代码按预期工作(当然还有其他方法)。

于 2010-03-09T18:53:22.380 回答
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

等等

于 2013-03-19T16:48:35.997 回答