有没有更好的替代方法,可以将可变的可选参数保留为参数,而其值不会在 Python 的后续调用中保留?

Is there a better alternative to keeping a mutable optional argument as a parameter without its value persisting on subsequent calls in Python?

提问人:Dan Chrostowski 提问时间:12/22/2016 最后编辑:Dan Chrostowski 更新时间:12/23/2016 访问量:225

问:

不恰当地标记为重复问题。这与不知道如何通过引用传递变量无关。它询问是否有一种方法可以缓解将可变可选参数保留为参数的问题,而不会在后续调用时将其值保留在函数的上下文中。

因此,我今天刚刚遇到了这种情况,现在我熟悉了许多新的 Python 开发人员陷入的陷阱:可变默认参数。为简洁起见,以下是我所讨论的内容的一个示例:

def MyClass(object):
        ...
    def my_func(self,my_str="default",my_dict={},**kwargs):
        my_dict.update(kwargs)
        self.my_dict = my_dict

----------------------------------------
<< a = MyClass()
<< a.my_func(foo='bar')
<< a.my_dict
>> {'foo':'bar'}

<< b = MyClass()
<< b.my_func(bar='baz')
<< b.my_dict
>> {'foo': 'bar', 'bar': 'baz'}

我知道这是故意的,并且理解一个好的解决方案是修改为此:my_func

def my_func(self,my_str="default",my_dict=None,**kwargs)
    if my_dict is None: my_dict = {}
                 ...

但是,我对此并不满意。它只是以错误的方式摩擦我。我不希望它默认为“无”。默认情况下,我想要一个空字典。于是我想到了这样做:

def my_func(self,my_str="default", my_dict={}, **kwargs)
    my_dict.update(kwargs)
    self.my_dict = my_dict
    my_dict = {}

但这当然行不通,你会得到和以前一样的行为。但是为什么?持久性和可变性不是吗?还是只在 Python 方便的时候?除了简单地排除为可选参数之外,还有其他方法可以处理这个问题吗?my_dictmy_dict

我真的很挂断这个,因为我发现使用默认参数将类型传达给读者很有用,这提高了代码的可读性 IMO。这很好,因为读者不需要深入研究该函数来确定它应该是一个字典(inb4 建议“只使用评论”——理想主义,但对我的目的来说不是很现实)。my_dict

这个“功能”毫无意义,而且对我来说非常令人反感,以至于我实际上非常接近相信它真的是一个伪装的错误。没有人会使用它,这对我来说几乎是一个交易破坏者。

提前致谢。

参数 可变 default-参数

评论

0赞 TigerhawkT3 12/22/2016
“没有人会用这个”——错了。我已经用过了。它类似于类属性与实例属性。
0赞 Dan Chrostowski 12/22/2016
夸张。我敢肯定,大多数 Python 开发人员,尤其是那些来自这不是功能的语言的开发人员,不会考虑使用它。
3赞 TigerhawkT3 12/22/2016
把你的意见当作事实,把任何不同意的人称为“fanboi”,并不会改变你问题的有效性,而且可能不会鼓励Python专家--对不起,fanbois--贡献他们的时间来帮助你。
1赞 TigerhawkT3 12/22/2016
反对票不是为了让任何人沉默,也不是针对你个人的。它们表明(如工具提示所示)在选民看来,该问题缺乏研究工作、清晰度或实用性。
1赞 TigerhawkT3 12/22/2016
我觉得很奇怪,你发现一种语言特征是“冒犯性的”,却给其他人贴上了“情绪化”的标签,而你声称提供了一种“建设性的......讨论的基础“,称人们为”fabois“。

答:

4赞 BrenBarn 12/22/2016 #1

如果我理解正确的话,您认为通过赋值 ,您正在“重置”值,以便下次调用该函数时,它将再次为空。my_dict = {}

这不是默认参数的工作方式。相反,它的工作原理是这样的:

  1. 定义函数时,将创建一个“secret”对象,该对象包含参数的默认值。(您可以将此秘密对象视为普通函数,尽管如果您正在查看方法对象,则会有一些额外的复杂性。func.__defaults__
  2. 每次调用该函数时,如果未传递给定参数的值,Python 会将该参数的值分配给以前存储的机密对象。

换句话说,Python 并没有真正存储跨调用的值。它存储一个默认值,并在每次调用时检索该值(除非您传递一个新值)。my_dict

当你这样做时,你只是给局部变量分配一个新值,这不会影响秘密对象。另一方面,当你做类似的事情时,你正在改变秘密对象。(如果你惊讶于赋值变量和调用方法有不同的效果,那么你应该在这里搜索很多关于赋值、变量绑定、可变对象与不可变对象等的 Python 问题。my_dict = {}my_dictmy_dict.update()update

评论

0赞 Natecat 12/22/2016
您可能想在回答中提及dictobj.clear()
0赞 BrenBarn 12/22/2016
@Natecat:那可能很危险。您是否想象在最后调用以重置值?如果实际传入非默认值,那就太糟糕了,因为它会擦除您作为参数传递的字典中的任何内容。my_dict.clear()
0赞 Dan Chrostowski 12/22/2016
@BrenBarn - 对于期望它存在的人来说,基本上是击败了该功能,那么?这就是我实际上想做的。
0赞 BrenBarn 12/22/2016
@2rare2die:不知道你的意思。这不仅仅是打败默认参数功能的问题;它会产生更严重的副作用。我的意思是,如果你这样做了,并且有人调用了,他们在你的函数之外创建了一些字典,那么你的函数将清除他们的字典。这通常是一个意想不到的副作用。my_dict.clear()my_func(my_dict=blah)blahblah
0赞 Dan Chrostowski 12/22/2016
@BrenBarn你的回答中有一些很好的见解,我正在获得一堆知识,这是我在问之前没有的。