Python 作用域/静态误解

发布于 2024-08-09 02:25:43 字数 967 浏览 7 评论 0原文

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

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

I'm really stuck on why the following code block 1 result in output 1 instead of output 2?

Code block 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,]

The goal of this code is to iterate through an array and wrap each in a parent object. This is a reduction of my actual code which adds all apples to a bag of apples and so forth. My guess is that, for some reason, it's either using the same object or acting as if the fruit container uses a static array. I have no idea how to fix this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

南笙 2024-08-16 02:25:43

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

改为这样做:

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

You should never use a mutable value (like []) for a default argument to a method. The value is computed once, and then used for every invocation. When you use an empty list as a default value, that same list is used every time the method is invoked without the argument, even as the value is modified by previous function calls.

Do this instead:

def __init__(self,arr=None):
    self.array = arr or []
萤火眠眠 2024-08-16 02:25:43

正如内德所说,问题是您使用列表作为默认参数。 此处有更多详细信息。解决方案是更改 __init__ 函数,如下所示:

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

As Ned says, the problem is you are using a list as a default argument. There is more detail here. The solution is to change __init__ function as below:

       def __init__(self,arr=None):
           if arr is not None:
               self.array = arr
           else:
               self.array = []
黯然 2024-08-16 02:25:43

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

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

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

Your code has a default argument to initialize the class. The value of the default argument is evaluated once, at compile time, so every instance is initialized with the same list. Change it like so:

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

I discussed this more fully here: How to define a class in Python

薯片软お妹 2024-08-16 02:25:43

比传入 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,因为您不小心给容器的内部存储起了别名。

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

A better solution than passing in None — in this particular instance, rather than in general — is to treat the arr parameter to __init__ as an enumerable set of items to pre-initialize the FruitContainer with, rather than an array to use for internal storage:

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

This will allow you to pass in other enumerable types to initialize your container, which more advanced Python users will expect to be able to do:

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

It will also defuse another potential aliasing bug:

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

With all other implementations on this page, this will return True, because you have accidentally aliased the internal storage of your containers.

Note: This approach is not always the right answer: "if not None" is better in other cases. Just ask yourself: am I passing in a set of objects, or a mutable container? If the class/function I'm passing my objects in to changes the storage I gave it, would that be (a) surprising or (b) desirable? In this case, I would argue that it is (a); thus, the list(...) call is the best solution. If (b), "if not None" would be the right approach.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文