Python 可选参数

24 投票
3 回答
32414 浏览
提问于 2025-04-15 15:09

大家好,我最近刚开始学习Python,遇到了一些关于可选参数的困惑。比如我有这样的程序:

class B:
   pass

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

如果我创建了A两次

b = A()
c = A()

然后打印它们的构建信息

print b.builds
print c.builds

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

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

但这不是我想要的,因为如果b改变了构建的某些内部状态,那么c对象里的那个也会被改变。

有没有办法每次都重新创建这个可选参数,使用这种可选参数的语法呢?

3 个回答

6

没错,默认参数只在函数定义的时候被计算一次。

一个可能的解决办法是把参数设置成一个,而不是一个实例,像这样:

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

你需要做以下几点:

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

这是一个很常见的错误,就是把可变的参数当作默认参数来使用。可能在StackOverflow上有很多类似的问题。

47

你需要了解默认值是怎么工作的,这样才能有效地使用它们。

函数其实是对象。因此,它们有一些属性。如果我创建了这个函数:

>>> 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.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]

了解这一点后,很明显如果你想让函数参数的默认值是一个新的列表(或者任何新的对象),你不能仅仅把这个对象的实例放在 func_defaults 里。你每次调用函数时都必须创建一个新的实例:

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

撰写回答