简体   繁体   English

使用继承,实例化,* args,** kwargs的代码混乱

[英]Confusion in code using inheritance, instantiation, *args, **kwargs

class Pen(object):
    def __init__(self, mean_radius = None, length = None):
        self.usage = "Is used to apply ink trails to drawing surfaces"
        self.mean_radius = mean_radius
        self.length = length

class FountainPen(Pen):
    def __init__(self, manufacturer = "Waterman", *args, **kwargs):
        Pen.__init__(self, *args, **kwargs)
        self.manufacturer = manufacturer
        self.cartridge_state =  "non-empty"

>>> instance_FP = FountainPen(5, 1)
>>> instance_FP.mean_radius
>>> print instance_FP.mean_radius
1    
>>> print instance_FP.length
None

What happens with the integer 5 being passed as an argument in the instantiation of FountainPen ? FountainPen的实例化中将整数5作为参数传递时会发生什么情况? Why does print instance_FP.mean_radius return 1 and not 5 ? 为什么print instance_FP.mean_radius返回1而不是5

You have to think it like that: *args and **kwargs "eat" all the positional/keyword arguments left by the "regular" arguments that precede them. 您必须这样认为: *args**kwargs “吃掉”在它们前面的“常规”参数留下的所有位置/关键字参数。 If you put them in the end, they will only get whatever couldn't fit the "regular arguments". 如果把它们放在最后,它们只会得到不适合“常规参数”的任何东西。

So, when you write FountainPen(5,1) what happens is that FountainPen.__init__ gets called like this: 因此,当您编写FountainPen(5,1) ,会发生FountainPen.__init__调用FountainPen.__init__情况:

  • self is set to the newly created instance; self设置为新创建的实例;
  • manufacturer gets the first argument, which is 5; manufacturer获得第一个参数,即5;
  • *args "eats" all the remaining positional arguments, ie just 1 ; *args “吃掉”所有剩余的位置参数,即仅1 so args is now set to [1] ; 因此args现在设置为[1]
  • *kwargs would eat any keyword argument left, but there's none, so it becomes {} . *kwargs会吃掉剩下的所有关键字参数,但是没有关键字,因此它变成{}

It's then clear that Pen.__init__ is called with just 1 as argument (besides self ), and length remains set to its default value ( None ). 然后很明显, Pen.__init__ 1作为参数(除了self )调用了Pen.__init__ ,而length仍设置为其默认值( None )。

Matteo Italia 's mentions what is wrong, basically manufacturer is a positional argument. Matteo Italia的提法出了什么问题,基本上manufacturer是一个立场论点。 You can fix it by slightly changing FountainPen 's __init__ to the following: 您可以通过将FountainPen__init__稍微更改为以下内容来修复它:

def __init__(self, *args, manufacturer = "Waterman", **kwargs):

Doing this will make manufacturer a keyword argument so to change it you will have to call: 这样做会使manufacturer成为关键字参数,因此要更改它,您必须调用:

FountainPen(manufacturer="newval")

Note: Unfortunately this syntax (of having keyword only arguments after the *args parameter) is only valid in Python 3. Jon Clements has a solution that solves this problem for Python 2. 注意:不幸的是,这种语法(在*args参数后仅具有关键字参数)仅在Python 3中有效。Jon Clements的解决方案可解决Python 2的此问题。

A more elegant solution would be: 一个更优雅的解决方案是:

class FountainPen(Pen):
    def __init__(self, *args, **kwargs):
        Pen.__init__(self, *args, **kwargs)
        self.manufacturer = kwargs.get('manufacturer', 'Waterman')
        self.cartridge_state =  "non-empty"

blah = FountainPen(5, 1, manufacturer='Waterman')

In your code 5 is assigned to the argument manufacturer and 1 goes to *args . 在您的代码中,将5分配给参数manufacturer ,将1分配给*args

When passing arguments without keys to a function, you must first provide the explicit ones. 将不带键的参数传递给函数时,必须首先提供显式参数。 The rest is passed to *args. 其余的传递给* args。 To avoid this, provide keywords when calling your constructor: 为避免这种情况,请在调用构造函数时提供关键字:

>>> instance_FP = FountainPen(mean_radius=5, length=1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM