2009-10-18 78 views
24

伙计们,我刚开始蟒蛇最近和感到困惑与可选参数,说我有一个这样的程序:Python的可选参数

class B: 
    pass 

class A: 
    def __init__(self, builds = B()): 
     self.builds = builds 

如果我创建一个两次

b = A() 
c = A() 

和打印他们建立

print b.builds 
print c.builds 

我发现他们使用的是完全相同的对象,

<__main__.B instance at 0x68ee0> 
<__main__.B instance at 0x68ee0> 

但它不是我想要的,因为如果b改变建立的一些内部状态,一个在c对象也随之发生变化。

是否有可能通过使用此可选参数语法每次重新创建此可选参数?

+15

如果解决了问题,您应该接受答案!几个答案似乎非常丰富。尽管这个问题还很陈旧,但我认为给出一个批准的答复是一个好主意 – eipxen 2011-11-29 18:31:45

+0

[[Python中的“最小的惊讶”:可变的默认参数]可能的重复(http://stackoverflow.com/问题/ 1132941/python-the-mutable-default-argument中的令人惊讶的事情) – 2013-07-12 00:06:22

回答

6

是的;默认参数仅在函数定义时进行评估。

一个可能的解决办法是有参数是类,而不是一个实例,一拉

def foo(blah, klass = B): 
    b = klass() 
    # etc 
+0

简而言之,你不能使用可变对象作为默认值。 – 2009-10-18 20:02:36

+0

当然可以。类在Python中是可变的。它与可变/不可变无关。 – 2009-10-18 20:13:18

+0

使用可变对象会导致此问题中的确切混淆。当你使用一个类作为默认时,你几乎总是实例化来创建一个对象。这个类的可变性并不重要,因为你不是直接使用类,而是使用列表或字典。 – 2009-10-18 22:09:33

15

,你需要做到以下几点:

class A: 
    def __init__(self, builds=None): 
     if builds is None: 
      builds = B() 
     self.builds = builds 

这是一个非常广泛的 - 扩展错误,使用可变参数作为默认参数。可能有很多愚蠢的东西。

+1

让我们找到一个dup并关闭这个? – 2009-10-18 16:22:27

+2

问题是这样的:你不能使用可变对象作为默认值。以前的重复都尝试使用列表。这是第一次使用对象。这是试图将可变对象用作默认值的“标准问题”。 – 2009-10-18 20:02:02

46

您需要了解的默认值,以便有效地使用它们是如何工作的。

功能是对象。因此,它们具有属性。所以,如果我创建这个功能:

>>> def f(x, y=[]): 
     y.append(x) 
     return y 

我已经创建了一个对象。这里是它的属性:

>>> dir(f) 
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', 
'__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__',  
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 
'func_name'] 

其中之一是func_defaults。听起来很有希望,那里有什么?这是一个包含函数默认值的元组。如果默认值是一个对象,则该元组包含该对象的一个​​实例。

这会导致一些非常违反直觉的行为,如果你认为f增加了一个项目列表,返回只包含项目的列表,如果没有提供列表:

>>> f(1) 
[1] 
>>> f(2) 
[1, 2] 

但是,如果你知道默认值是一个储存在该函数的属性之一的对象实例,它更是违反直觉:

>>> x = f(3) 
>>> y = f(4) 
>>> x == y 
True 
>>> x 
[1, 2, 3, 4] 
>>> x.append(5) 
>>> f(6) 
[1, 2, 3, 4, 5, 6] 

认识到这一点,很明显,如果你想要一个函数的参数的默认值是一个新的列表(或任何新的ob ject),你不能简单地在func_defaults中存储对象的一个​​实例。每次调用函数时都必须创建一个新的函数:

>>>def g(x, y=None): 
     if y==None: 
      y = [] 
     y.append(x) 
     return y