1
  1 import sys
  2 
  3 class dummy(object):
  4     def __init__(self, val):
  5         self.val = val
  6 
  7 class myobj(object):
  8     def __init__(self, resources):
  9         self._resources = resources
 10 
 11 class ext(myobj):
 12     def __init__(self, resources=[]):
 13         #myobj.__init__(self, resources)
 14         self._resources = resources
 15 
 16 one = ext()
 17 one._resources.append(1)
 18 two = ext()
 19 
 20 print one._resources
 21 print two._resources
 22 
 23 sys.exit(0)

这将打印对分配给 和 对象的对象one._resourcesone引用two。我认为这two将是一个空数组,因为如果在创建对象时未定义它,它显然是这样设置的。取消注释myobj.__init__(self, resources)做同样的事情。使用super(ext, self).__init__(resources)也做同样的事情。

我可以让它工作的唯一方法是如果我使用以下内容:

two = ext(dummy(2))

在创建对象以使其工作时,我不必手动设置默认值。或者也许我会。有什么想法吗?

我使用 Python 2.5 和 2.6 进行了尝试。

4

5 回答 5

7

你应该改变

def __init__(self, resources=[]):
    self._resources = resources

def __init__(self, resources=None):
    if resources is None:
        resources = []
    self._resources = resources

一切都会更好。如果默认参数是可变的,这是处理默认参数的方式的一个细节。本页的讨论部分提供了更多信息。

于 2009-10-07T21:51:46.733 回答
6

您的问题是在函数定义时评估默认值。这意味着在实例之间共享相同的列表对象。有关更多讨论,请参阅此问题的答案。

于 2009-10-07T21:51:54.720 回答
2

请阅读此答案以讨论如何从__init__(). 您遇到了 Python 的一个众所周知的怪癖:您正在尝试设置一个 mutable,并且您的 mutable 在__init__()编译时被评估一次。标准的解决方法是:

class ext(myobj):
    def __init__(self, resources=None):
        if resources is None:
            resources = []
        #myobj.__init__(self, resources)
        self._resources = resources
于 2009-10-07T21:55:07.763 回答
1

来自http://docs.python.org/3.1/tutorial/controlflow.html

默认值仅评估一次。当默认值是可变对象(例如列表、字典或大多数类的实例)时,这会有所不同。

于 2009-10-07T21:52:40.353 回答
0

这是一个已知的Python陷阱

您必须避免在调用函数/方法时使用可变对象。

在调用函数/方法时不会创建提供默认值的对象。它们是在执行定义函数的语句时创建的。(请参阅Python 中的默认参数的讨论:两个简单的错误“默认参数中的表达式是在定义函数时计算的,而不是在调用它时计算的。”

这种行为在 Python 语言中并不是一个缺陷。它确实是一个功能,而不是一个错误。有时您确实想使用可变的默认参数。他们可以做的一件事(例如)是保留以前调用的结果列表,这可能非常方便。

但是对于大多数程序员——尤其是 Python 初学者——这种行为是一个陷阱。因此,对于大多数情况,我们采用以下规则。

  • 永远不要使用可变对象(即:列表、字典或类实例)作为参数的默认值。
  • 只有当你真的、真的、真的知道你在做什么时,才忽略规则 1。

所以...我们计划始终遵循规则#1。现在,问题是如何去做……如何编写 functionF 代码以获得我们想要的行为。

幸运的是,解决方案很简单。用作默认值的可变对象被替换为None,然后对参数进行测试None


那么如何才能正确地做到这一点呢?一种解决方案是避免对 arguments 使用可变的默认值。但这并不令人满意,因为有时新列表是一个有用的默认值。有一些复杂的解决方案,例如为深度复制所有参数的函数定义装饰器。这是一个矫枉过正,问题可以很容易地解决如下:

class ext(myobj):
    def __init__(self, resources=None):
        if not resources:
            resources = []
        self._resources = resources
于 2009-10-07T22:08:08.127 回答