简体   繁体   中英

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 ? Why does print instance_FP.mean_radius return 1 and not 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. 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:

  • self is set to the newly created instance;
  • manufacturer gets the first argument, which is 5;
  • *args "eats" all the remaining positional arguments, ie just 1 ; so args is now set to [1] ;
  • *kwargs would eat any keyword argument left, but there's none, so it becomes {} .

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 ).

Matteo Italia 's mentions what is wrong, basically manufacturer is a positional argument. You can fix it by slightly changing FountainPen 's __init__ to the following:

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

Doing this will make manufacturer a keyword argument so to change it you will have to call:

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.

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 .

When passing arguments without keys to a function, you must first provide the explicit ones. The rest is passed to *args. To avoid this, provide keywords when calling your constructor:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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