Python方法*参数*中的双下划线

15

我知道双下划线对于Python类的属性和方法有什么含义,但它对于方法参数是否有特殊含义呢?

看起来你不能将以双下划线开头的参数传递给方法。这很令人困惑,因为你可以在普通函数中这样做。

考虑以下脚本:

def egg(__a=None):
    return __a

print "egg(1) =",
print egg(1)
print


class Spam(object):

    def egg(self, __a=None):
        return __a

print "Spam().egg(__a=1) =",
print Spam().egg(__a=1)

运行这个脚本会产生以下结果:

egg(1) = 1

Spam().egg(__a=1) =
Traceback (most recent call last):
  File "/....py", line 15, in <module>
    print Spam().egg(__a=1)
TypeError: egg() got an unexpected keyword argument '__a'

我使用Python 2.7.2进行了测试。


以下是一些其他的示例

这个可以工作:

def egg(self, __a):
    return __a


class Spam(object):

    egg = egg

Spam().egg(__a=1)

这不会:

class Spam(object):

    def _egg(self, __a=None):
        return __a

    def egg(self, a):
        return self._egg(__a=a)

Spam().egg(1)
3个回答

13

名称修饰适用于所有具有前导双下划线的标识符,无论它们出现在哪里(该部分倒数第二句话):

这种转换与标识符在其使用的语法上下文无关。

这样实现和定义更简单、更一致。这可能看起来很愚蠢,但整个名称修饰问题在我看来是一个丑陋的小技巧;你不应该为任何除了属性/方法以外的东西使用这样的名称。

Spam().egg(_Spam__a=1),以及Spam().egg(1),都可以工作。但即使你可以让它工作,在参数名称中,前导下划线(任意数量)也没有任何地方。或者在任何局部变量中(异常:_)。

编辑:你似乎发现了一个没有人考虑过的特殊情况。这里的文档不够精确,或者实现有缺陷。它似乎关键字参数名称没有被修改。看看字节码(Python 3.2):

>>> dis.dis(Spam.egg)
  3           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (_egg)
              6 LOAD_CONST               1 ('__a') # keyword argument not mangled
              9 LOAD_FAST                1 (a)
             12 CALL_FUNCTION          256
             15 RETURN_VALUE
>>> dis.dis(Spam._egg)
  2           0 LOAD_FAST                1 (_Spam__a) # parameter mangled
              3 RETURN_VALUE

这可能源于关键字参数相当于传递一个字典(在本例中为{'__a': 1}),其键也不会被篡改。但老实说,我只是把它称为已经很丑陋的特殊情况中的一个丑陋角落,并继续前进。这并不重要,因为你无论如何都不应该使用那样的标识符。

如果它发生“无论它们发生在哪里”,我希望最后一个示例(我添加的那个)可以工作。不过我想我知道为什么它不起作用;它应该在“类命名空间”或类似的东西中(我不知道正确的术语是什么),对吗? - tkf
我也会这样期望,并感到非常惊讶。我猜这可能是文档或实现上的缺陷。我正在编辑以添加我的发现。 - user395760
1
我同意这不是一个优雅的参数,但是考虑到当你在函数中有像self.__dict__.update(kwds)这样的东西,但是想要控制函数的行为时。我能想到唯一有效的用于控制行为的参数名称应该以双下划线开头。 - tkf
@tkf 位置参数怎么样?单个下划线也可以,因为它们也不常见,但我认为保持控制参数和kwargs尽可能不同非常重要,这样读者就可以轻松区分它们。 - user395760

3
它被转换为_Spam__a:
In [20]: class Spam(object):
   ....:     
   ....:         def egg(self, __a=None):
   ....:             return __a
   ....: 

In [21]: Spam.egg.__func__.__code__.co_varnames
Out[21]: ('self', '_Spam__a')

1

在类上下文中,双下划线或名称修饰是一个私有标识符。在您的示例中尝试使用dir(Spam.egg),您会发现参数__a现在变成了_Spam__egg

现在您可以使用:

Spam().egg(_Spam__a=1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接