两个关键字参数位置互换时发生错误

0 投票
3 回答
1480 浏览
提问于 2025-04-15 19:06

我遇到了一个奇怪的问题。我知道在Python中,关键字参数(kwargs)是跟在位置参数(args)后面的,所以我检查了这一点,发现这不是问题所在。问题在于:

这个是正常的:

def __init__(self, sample_rate, label=u"", data=[] ):

但是出现了这个错误:__init__() 收到了多个 'data' 关键字参数的值:

def __init__(self, sample_rate, data=[], label=u""):

导致错误的调用行看起来是这样的:

def __getslice__(self, start, stop):
    return Channel(self.sample_rate, self.label, data=list.__getslice__(self,start,stop))

完整的代码是:

class Channel(list):
  sample_rate = 0

  def __init__(self, sample_rate, data=[], label=u"" ):
     list.__init__(self,data)
     self.sample_rate = sample_rate
     self.label = label

  @property
  def nyquist_rate(self):
      return float(self.sample_rate) / 2.0

  def __getslice__(self, start, stop):
      return Channel(self.sample_rate, self.label, data=list.__getslice__(self,start,stop))

谢谢!

3 个回答

2

问题在于你的调用代码中,有两个位置参数:

return Channel(self.sample_rate, self.label, data=list.__getslice__(self,start,stop))
#              sample_rate (pos) data (pos)  data (kw)

在Python 2.x中,函数定义时位置参数和关键字参数没有区别。当你调用一个函数时,位置参数会从左到右依次填充函数的参数,然后再绑定所有的关键字参数。在你的例子中,data同时被位置参数和关键字参数绑定了。在另一种情况下,它之所以能正常工作,是因为第二个位置参数被用于label,而data只接收了关键字参数。

4

你是这样调用代码的:

Channel(self.sample_rate, self.label, data=list.__getslice__(self,start,stop))

注意,第二个参数没有指定关键字,所以解释器会认为这是 data 参数(因为在函数定义时它们的顺序就是这样)。如果你加上 label=,应该就能解决这个问题。

不过,你的代码里还有一个更重要的错误:不要把 [] 作为默认值。原因是这个代码在定义函数的时候就会被计算。每次你调用这个代码而没有提供 data 参数时,你会得到同一个列表作为默认值。而且第一次之后,这个列表可能就不再是空的了!这对所有可变数据类型都是这样的。正确的做法是,把 None 作为默认值,然后在函数内部(每次运行的代码)检查参数值是否为 None,如果是的话就初始化一个新的 []。

(这个问题也被 David Goodger 解释得很好,详细内容可以查看 默认参数值

4

在第二个版本中(def __init__(self, sample_rate, data=[], label=u""):),第二个位置参数(也就是在调用时,不算self)是data,但是在__getslice__中,你传入的第二个参数是label。所以你要么保持label作为第二个参数,要么把函数调用改成这样:

return Channel(self.sample_rate, label=self.label, data=list.__getslice__(self,start,stop))

撰写回答