Python范围/静态误解

时间:2009-10-31 17:11:28

标签: python class scope iteration static-members

我真的坚持为什么以下代码块1导致输出1而不是输出2?

代码块1:

class FruitContainer:
       def __init__(self,arr=[]):
           self.array = arr
       def addTo(self,something):
           self.array.append(something)
       def __str__(self):
           ret = "["
           for item in self.array:
               ret = "%s%s," % (ret,item)
           return "%s]" % ret

arrayOfFruit = ['apple', 'banana', 'pear']
arrayOfFruitContainers = []

while len(arrayOfFruit) > 0:
   tempFruit = arrayOfFruit.pop(0)
   tempB = FruitContainer()
   tempB.addTo(tempFruit)
   arrayOfFruitContainers.append(tempB)

for container in arrayOfFruitContainers:
   print container 

**Output 1 (actual):**
[apple,banana,pear,]
[apple,banana,pear,]
[apple,banana,pear,]

**Output 2 (desired):**
[apple,]
[banana,]
[pear,]

此代码的目标是遍历数组并将每个数据包装在父对象中。这是我的实际代码的减少,它将所有苹果添加到一袋苹果等等。我的猜测是,出于某种原因,它要么使用相同的对象,要么就像水果容器使用静态数组一样。我不知道如何解决这个问题。

4 个答案:

答案 0 :(得分:8)

您永远不应该使用可变值(如[])作为方法的默认参数。该值计算一次,然后用于每次调用。当您使用空列表作为默认值时,每次在没有参数的情况下调用该方法时都会使用相同的列表,即使该值已被先前的函数调用修改。

请改为:

def __init__(self,arr=None):
    self.array = arr or []

答案 1 :(得分:2)

您的代码有一个默认参数来初始化类。默认参数的值在编译时计算一次,因此每个实例都使用相同的列表进行初始化。像这样改变:

def __init__(self, arr=None):
    if arr is None:
        self.array = []
    else:
        self.array = arr

我在这里更充分地讨论了这个问题: How to define a class in Python

答案 2 :(得分:1)

正如Ned所说,问题是你使用列表作为默认参数。还有更多细节here。解决方案是更改__init__功能,如下所示:

       def __init__(self,arr=None):
           if arr is not None:
               self.array = arr
           else:
               self.array = []

答案 3 :(得分:0)

比传入None更好的解决方案 - 在这个特定的实例中,而不是一般 - 是将arr参数作为一个可枚举的项集来处理__init__,以预先初始化FruitContainer,而不是用于内部存储:

class FruitContainer:
  def __init__(self, arr=()):
    self.array = list(arr)
  ...

这将允许您传入其他可枚举类型来初始化容器,这是更高级的Python用户期望能够做到的:

myFruit = ('apple', 'pear') # Pass a tuple
myFruitContainer = FruitContainer(myFruit)
myOtherFruit = file('fruitFile', 'r') # Pass a file
myOtherFruitContainer = FruitContainer(myOtherFruit)

它还将化解另一个潜在的别名错误:

myFruit = ['apple', 'pear']
myFruitContainer1 = FruitContainer(myFruit)
myFruitContainer2 = FruitContainer(myFruit)
myFruitContainer1.addTo('banana')
'banana' in str(myFruitContainer2)

对于此页面上的所有其他实现,这将返回True,因为您不小心将容器的内部存储设为别名。

注意:此方法总是正确答案:“if not None”在其他情况下更好。只要问问自己:我是否传递了一组对象或一个可变容器?如果我传递我的对象的类/函数改变了我给它的存储,那会是(a)令人惊讶还是(b)可取的?在这种情况下,我认为它是(a);因此,list(...)调用是最佳解决方案。如果(b),“如果不是无”将是正确的方法。