6

我真的很困惑为什么下面的代码块 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

4 回答 4

8

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

改为这样做:

def __init__(self,arr=None):
    self.array = arr or []
于 2009-10-31T17:14:37.377 回答
2

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

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

我在这里更全面地讨论了这个问题:如何在 Python 中定义一个类

于 2009-10-31T17:19:28.443 回答
1

正如 Ned 所说,问题在于您使用列表作为默认参数。这里有更多细节。解决方案是更改__init__功能如下:

       def __init__(self,arr=None):
           if arr is not None:
               self.array = arr
           else:
               self.array = []
于 2009-10-31T17:18:26.753 回答
0

比传入 None 更好的解决方案——在这个特定的实例中,而不是一般情况下——是将 __init__ 的 arr 参数视为一组可枚举的项目来预初始化 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,因为您不小心为容器的内部存储设置了别名。

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

于 2009-11-01T11:20:50.563 回答