Python对象初始化错误。或者我误解了对象是如何工作的?

时间:2009-10-07 21:45:19

标签: python mutable arguments

  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尝试过这个。

5 个答案:

答案 0 :(得分:7)

你应该改变

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

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

一切都会好起来的。这是默认参数处理方式的细节,如果它们是可变的。 this page的讨论部分提供了更多信息。

答案 1 :(得分:6)

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

答案 2 :(得分:2)

有关如何从__init__()设置课程的讨论,请阅读this answer。您遇到了一个众所周知的Python怪癖:您正在尝试设置一个mutable,并且在编译__init__()时会对您的mutable进行一次评估。标准解决方法是:

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

答案 3 :(得分:1)

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

  

仅评估默认值   一旦。这有所不同   default是一个可变对象,如a   列表,字典或大多数实例   类。

答案 4 :(得分:0)

这是known Python gotcha

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

  

在调用函数/方法时,不会创建提供默认值的对象。 它们是在执行定义函数的语句时创建的。 (参见Default arguments in Python: two easy blunders上的讨论:“默认参数中的表达式是在定义函数时计算的,而不是在调用函数时计算的。”

     

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

     

但是对于大多数程序员来说 - 特别是从Pythonistas开始 - 这种行为是一个问题。因此,对于大多数情况,我们采用以下规则。

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

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

     

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


  

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

class ext(myobj):
    def __init__(self, resources=None):
        if not resources:
            resources = []
        self._resources = resources