python超级调用中的*args和**kwds

python超级调用中的*args和**kwds,python,Python,我试图理解在Python中创建子类时,*args和**kwds的用法 我想了解为什么这段代码会有这样的行为。如果我在调用super()时省略了*args和**kwds。\uuuu init\uuu,我会得到一些奇怪的参数 以下是我的测试用例: 类动物(对象): 定义初始(自我、移动、腿数): self.moves=移动 self.num\u legs=num\u legs def描述(自我): 打印“Moves:{},num_legs:{}”。格式(self.Moves,self.num_leg

我试图理解在Python中创建子类时,
*args
**kwds
的用法

我想了解为什么这段代码会有这样的行为。如果我在调用
super()时省略了
*args
**kwds
。\uuuu init\uuu
,我会得到一些奇怪的参数

以下是我的测试用例:

类动物(对象):
定义初始(自我、移动、腿数):
self.moves=移动
self.num\u legs=num\u legs
def描述(自我):
打印“Moves:{},num_legs:{}”。格式(self.Moves,self.num_legs)
蛇类(动物):
定义初始值(自身、有毒、*args、**kwds):
有毒的
打印“我有毒:{}”。格式(self.toxing)
#下一行是关键。您必须使用*args、**kwds。
#但这里我故意用了不正确的形式,
#“args”和“kwds”,我对它的功能感到惊讶。
超级(蛇,自我)。\uuuuu初始(args,kwds)
现在,当我创建Snake子类的实例时,它包含对
super(…)的错误调用。uuu init_uuu
(我使用
args
kwds
而不是
*args
**kwds
),我得到了一些有趣的“参数解包”

s1=Snake(False,moves=True,num_legs=0)
s2=蛇(有毒=假,移动=真,腿数=1)
s3=蛇(假、真、3)
s1.描述()
s2.描述()
s3.描述()
我得到的是:

Moves:(),num_legs:{'Moves':True,'num_legs':0}
移动:(),num_legs:{'Moves':True,'num_legs':1}
移动:(真,3),腿数:{}
那么,为什么在
s1
s2
中,
\uuuuu init\uuuuu
假设
moves=True
num\u legs=0
1
是关键字参数,并将
num\u legs
设置为dict

s3
中,它将两个变量作为元组解压到
moves
(在类
Animal
)中



当我试图理解这个论点时,我无意中发现了这一点。事先抱歉-我不知道如何更好地界定这个问题。

简而言之:
def\uuuu init\uuuu(self,toxing,*args,**kwds):
意思是:在元组
args
中捕获位置参数,在字典
kwds
中捕获关键字参数。类似地,
super(Snake,self)。\uuuu init\uu(*args,**kwds)
的意思是:将元组
args
和字典
kwds
解包为参数,以便将它们分别传递给
\uu init\ucode>

如果不使用
*
**
则传递的是
args
kwds
,这意味着您将获得一个元组和一个字典


正如你所说,你需要写:

super(Snake,self).__init__(*args, **kwds)
正确打包/解包参数。在当前代码中,您没有打包/解包参数,因此它会将
num_legs
设置为字典,因为这就是当前的
kwds


如果不给参数命名,则它们是位置参数。因此
Snake(False,True,3)
都是位置参数

如果您确实给出了参数名称,那么它们就是关键字参数:
Snake(toxity=False,moves=True,num_legs=1)


在第一种情况下,您将组合一个位置参数和两个关键字参数:
Snake(False,moves=True,num\u legs=0)
Snake中,
args
有毒
之后所有位置参数的元组,
kwds
是除
有毒
之外所有关键字参数的dict。打电话

super(Snake,self).__init__(args,kwds)
您可以在
动物中为
移动指定
args
,为
腿数指定
kwds
。这正是您在输出中看到的


前两个调用除了
有毒
之外没有任何位置参数,因此
args
和相应的
移动
是一个空元组。第三个调用没有关键字参数,因此
kwds
和相应的
num_legs
是一个空命令。

与此
Snake(False,True,3)
相比,变异性更好、更直观:

哦,关于Python的问题真是令人兴奋,不仅仅是关于编程的问题。:)

在第一个位置设置最独立的参数是一个好主意,这对于所有子类都是通用的,就像通用的“自我”本身一样。下一个非常常见的名称与本例类似

如果您相信您的类永远不会被修改,并且它们每次都会与所有实现的参数一起使用,并且您永远不会以正确的顺序出错,那么您就不需要任何可变性。可以在使用时继续使用固定位置参数。这一假设经常得不到满足。明天,没有人会记得什么应该是第一个假,第二个真,而不看到它与关键字一起

如果需要通过
Snake(False,True,3)
调用具有固定位置参数的类,则不能对这些参数中的任何一个使用
**kwds

A)
现在让我们期待您的示例
Snake(False,True,3)
是一个必需的测试用例。然后,您不能对任何位置参数使用**kwds
(有毒、移动、腿数)
。您只有以下四种实现可能性_init__头:(都不够好)

.
B)
关键字参数更稳健,因为它们的一些错误可以自动报告。 期望您重新定义动物以增加其易变性:

class Animal(object):
    def __init__(self,name, moves=True, num_legs=None):
        self.name = name
        self.moves = moves
        self.num_legs = num_legs

# The recommended Snail  !
class Snake(Animal):
    def __init__(self, *args, **kwds):
        """Snake: Implements.. (Docs important, otherwise real keywords not seen in help)

        kwds:                (only what defined here)
            poisonous:  Bla bla. default=True
            constrictor:  Bla bla bla. default=False
        """
        # A copy of kwds can be created, if manipulation with original is prohibited.
        self.poisonous = kwds.pop('poisonous', True)  # default: poisonous snake
        self.constrictor = kwds.pop('constrictor', False)
        # OK. This reports error if some keyword is misspelled and will not be consumed.
        super(Snake,self).__init__(*args, **kwds)

# This Snake is more readable, but its descendants would be more complicated,
# otherwise is possible: "TypeError: got multiple values for keyword argument 'xy'".
class Snake(Animal):
    def __init__(self, name, poisonous=True, constrictor=False, *args, **kwds):
        self.poisonous = poisonous
        self.constrictor = constrictor
        super(Snake,self).__init__(name, *args, **kwds)

现在你有了很大的可变性,关键字参数的顺序并不重要。

阅读
super(Snake,self)一行之前的注释。
@flornquake:这很公平。那样的话,你就
# the most fragile solution - easy extensible, not easy to observe the order
class Snake(Animal):
    def __init__(self, *args):
        self.poisonous = args.pop[0]
        # or better ...pop[-1]  that allows adding new parameters to the end
        super(Snake,self).__init__(*args)
        # now is args undefined if ancestors could eat parts from it but
        # everything is in self

# the most naive solution - easy readable, not easy extensible because not DRY
class Snake(Animal):
    def __init__(self, poisonous, moves, num_legs):
        self.poisonous = poisonous
        super(Snake,self).__init__(moves, num_legs)

# anythig between them combines disadvantages of both previous
class Snake(Animal):
    def __init__(self, poisonous, *args):
        self.poisonous = poisonous
        super(Snake,self).__init__(*args)
class Snake(Animal):
    def __init__(self, poisonous, moves, *args):
        self.poisonous = poisonous
        super(Snake,self).__init__(moves, *args)
class Animal(object):
    def __init__(self,name, moves=True, num_legs=None):
        self.name = name
        self.moves = moves
        self.num_legs = num_legs

# The recommended Snail  !
class Snake(Animal):
    def __init__(self, *args, **kwds):
        """Snake: Implements.. (Docs important, otherwise real keywords not seen in help)

        kwds:                (only what defined here)
            poisonous:  Bla bla. default=True
            constrictor:  Bla bla bla. default=False
        """
        # A copy of kwds can be created, if manipulation with original is prohibited.
        self.poisonous = kwds.pop('poisonous', True)  # default: poisonous snake
        self.constrictor = kwds.pop('constrictor', False)
        # OK. This reports error if some keyword is misspelled and will not be consumed.
        super(Snake,self).__init__(*args, **kwds)

# This Snake is more readable, but its descendants would be more complicated,
# otherwise is possible: "TypeError: got multiple values for keyword argument 'xy'".
class Snake(Animal):
    def __init__(self, name, poisonous=True, constrictor=False, *args, **kwds):
        self.poisonous = poisonous
        self.constrictor = constrictor
        super(Snake,self).__init__(name, *args, **kwds)