为什么`*args`的值很奇怪?

4

这是我的代码快照:

class C(dict):
    def __init__(*args, **kwds):
        print 'args = {}'.format(args)
        print 'kwds = {}'.format(kwds)
        if not args:
            raise TypeError("descriptor '__init__' of 'Counter' object needs an argument")

        self = args[0]

        args = args[1:]

        if len(args) > 1:
            raise TypeError('expected at most 1 arguments, got %d' % len(args))

        super(C, self).__init__()


if __name__ == '__main__':
    c = C()

当我运行这段代码时,输出让我感到困惑:
C:\Users\elqstux\Desktop>python wy.py
args = ({},)
kwds = {}

对于 c = C() ,我认为代码会抛出错误,因为在这种情况下 args 将是(),但从输出中可以看到, args 是({},),原因是什么? self,args = args [0],args [1:]#允许传递“self”关键字

@Signal,我知道*args会是一个元组,但为什么在我的情况下它是({},),而不是() - BlackMamba
1
@Signal 无效的重复。 - Antti Haapala -- Слава Україні
3个回答

6
*args({},),因为你没有声明一个明确的self参数。所以当调用__init__时,self参数最终成为args中的第一个元素。由于你继承了dict,所以你得到了dictrepr,也就是{}
现在Python的dict子类中有意使用这个技巧,因为否则用mydict(self=123)初始化dict子类的代码将会出错。解决方法是:
 def __init__(*args, **kwargs):
     self, *args = args
     # On Python 2 without unpacking generalizations you'd use the more repetitive:
     # self, args = args[0], args[1:]

这迫使self纯粹被接受为一个位置参数(由实例构造机制隐式传递)。有一个真实世界的用例,可以参考 collections.Counter的实现


为什么如果我写成 def __init__(self, *args, **kwds):,那么 mydict(self=123) 就会破坏旧代码?使用 def __init__(*args, **kwds): 的目的是什么? - BlackMamba
1
@BlackMamba:你可以自己试一下。问题在于当你通过名称接受 self 时,它既可以按位置传递(由 Python 初始化机制),也可以按关键字传递,而执行 mydict(self=123) 现在尝试传递 self 两次(而不是创建一个带有键 'self'dict),这会触发 TypeError: __init__() got multiple values for argument 'self'。Python 不够聪明,无法使用位置参数 self 并将关键字参数 self 放入 kwargs 中,因此您需要帮助它。 - ShadowRanger
self, args = args[0], args[1:] # allow the "self" keyword be passed· - BlackMamba
@BlackMamba:这是在Python 2中必须这样做的方式;在Python 3中,使用*在普通表达式(不仅仅是函数调用)中是可能的(并且在3.5中几乎完全泛化),所以你可以做self, *args = args,这样更少重复,并且推广到其他情况(在这种情况下,args始终是一个序列,因此切片是可能的,但不能用迭代器/生成器来完成,而x,*y,z = mygeneratorfunction()在Py3中仍然无缝快速地工作,但需要丑陋/慢的方式:gen = mygeneratorfunction()x = next(gen)y = list(gen)z = y.pop()才能在Py2上工作)。 - ShadowRanger

3
我被你的输出结果吸引了!原因很明显:你在__init__函数定义中省略了self。使用普通类运行示例将清楚地说明发生了什么:
>>> class C():
...   def __init__(*args, **kwargs):
...     print args, kwargs
...
>>> C()
(<__main__.C instance at 0x103152368>,) {}
<__main__.C instance at 0x103152368>

当然,self作为args元组的唯一元素被困在其中。毕竟,self就像任何其他参数一样。
在您的情况下,让人更加困惑的是,由于您继承了dict类,因此此时您类的repr()是空字典{}repr()。因此出现了({},)

1
您的类__init__函数被调用时会带有一个参数,即隐含的self参数(即正在创建的新类实例)。通常情况下,这将打印为类实例。但由于该类是dict的子类,因此它会作为一个dict打印,最初没有任何元素。

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