关于python:调用super()和多重继承?

Calling super().__init__(**kwargs), and multiple inheritance?

我正在努力学习和理解如何在Python中使用super,我一直在遵循"从新手到专家的Python旅程",尽管我觉得我理解这个概念,但在自己的代码中执行super时遇到了问题。

例如,此方法适用于我:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Employee:        
    def __init__(self, firstname, lastname, age, sex, dob):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age
        self.sex = sex
        self.dob = dob
        self.all_staff.append(self)

class Hourly(Employee):
    def __init__(self, firstname, lastname, age, sex, dob, rate, hours):
        self.rate = rate
        self.hours = hours
        super().__init__(firstname, lastname, age, sex, dob)

    def __str__(self):
    return"{} {}
Age: {}
Sex: {}
DOB: {}
"
.format(self.firstname, self.lastname, self.age,
        self.sex, self.dob)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

print(hourlystaff1)

print(hourlystaff1.get_rate())

返回以下内容:

1
2
3
4
5
6
7
Bob Foo
Age: 23
Sex: M
DOB: 12/1/1980

The hourly rate of Bob is $15
None

这正是我所期望的(我不确定为什么"无"也会被退回,也许有人能解释?).

然后我想用super来试试这个,但是要用**kwargs,就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Employee:
    def __init__(self, firstname='', lastname='', age='', dob='', **kwargs):
        super().__init__(**kwargs)
        self.firstname = firstname
        self.lastname = lastname
        self.age = age
        self.dob = dob

class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        super().__init__(**kwargs)

    def __str__(self):
        return"{} {}
Age: {}
Sex: {}"
.format(self.firstname, self.lastname, self.age,
            self.sex, self.dob, self.rate)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

bob = Hourly('Bob', 'Bar', '23', '12/1/2019')


bob.get_rate('$12')

返回此错误:

1
2
3
  File"staff_b.py", line 33, in <module>
    bob = Hourly('Bob', 'Bar', '23', '12/1/2019')
TypeError: __init__() takes from 1 to 2 positional arguments but 5 were given

在第二种方法中我做错了什么?如何正确使用**Kwargs和Super?

编辑:

这是本书中一个示例的屏幕截图,我一直在关注:

enter image description here

在第二个例子中,我如何使用**Kwargs和super有什么不同?

这也是同一本书和同一章的综合案例研究。这对我有用,我理解它是如何工作的,但我似乎无法将它翻译成我自己的作品。

https://pastebin.com/nygjfmik网站


你在这里看到的水泡并不是针对超级水泡,而是针对夸格斯水泡。如果我们丢弃了您的大部分代码并删除super,它看起来是这样的:

1
2
3
4
5
6
7
class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        some_crazy_function(**kwargs)

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

有两个明显的问题:__init__函数传递的参数比预期的多,并且在__init__函数的主体中是对kwargs的引用,它在任何地方都没有定义。在这里,了解**kwargs和它的兄弟*args就足以解决这个问题,而super和**kwargs在一起非常有用。我们先来看看为什么super是有用的。假设我们用一些好的辅助方法围绕子流程编写一些包装(体系结构可能不是最适合这个问题的方法,但是只有见过具有继承性的动物才是没有帮助的)。多重继承是一种非常罕见的情况,因此很难找到不是动物、游戏实体或小部件的好例子):

1
2
3
4
5
6
7
8
9
class Process:
    def __init__(self, exe):
        self.exe = exe
        self.run()

class DownloadExecutableBeforeProcess(Process):
    def __init__(self, exe):
        self.download_exe(exe)
        Process.__init__(self, exe)

这里我们正在进行继承,甚至不需要使用super——我们可以显式地使用superclass的名称,并拥有我们想要的行为。我们可以在这里重写使用super,但它不会改变行为。如果您只继承一个类,那么您不需要严格地使用super,尽管它可以帮助您不重复继承的类名。让我们添加到我们的类Hirarchy,并包括从多个类插入:

1
2
3
4
5
6
7
8
9
class AuthenticationCheckerProcess(Process):
    def __init__(self, exe, use_sha=True):
        self.check_if_authorized(exe, use_sha)
        Process.__init__(self, exe)

class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        DownloadExecutableBefore.__init__(exe)
        AuthenticationCheckerProcess.__init__(exe, use_sha=False)

如果按照EDOCX1的初始值(9),我们会看到Process.__init__被调用两次,一次通过DownloadExecutableBefore.__init__,一次通过AuthenticationCheckerProcess.__init__!所以我们想要包装的过程也运行了两次,这不是我们想要的。在这个例子中,我们可以不在进程的初始阶段调用self.run(),很容易地解决这个问题,但是在现实世界中,这并不总是像这里那样容易解决。在这种情况下,打电话给Process.__init__似乎是错误的。我们能解决这个问题吗?

1
2
3
4
class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        super().__init__(exe, use_sha=False)
        # also replace the Process.__init__ cals in the other classes with super

super解决了这个问题,只调用Process.__init__一次。它还将处理函数的运行顺序,但这不是一个大问题。我们还有一个问题:use_sha=False将被传递到所有初始值设定项,但实际上只有一个需要它。我们不能只将变量传递给需要它的函数(因为认为这样做会是一场噩梦),但是我们可以教其他__init__s忽略keywoard:

1
2
3
4
5
6
7
8
9
10
11
12
class Process:
    def __init__(self, exe, **kwargs):
        # accept arbitrary keywoards but ignore everything but exe
        # also put **kwargs in all other initializers
        self.exe = exe
        self.run()

class DownloadExecutableBeforeProcess(Process):
    def __init__(self, exe, **kwargs):
        self.download_exe(exe)
        # pass the keywoards into super so that other __init__s can use them
        Process.__init__(self, exe, **kwargs)

现在,super().__init__(exe, use_sha=False)调用将成功,每个初始值设定项只获取它理解的关键字,并将其他关键字进一步传递下去。

因此,如果您有多个继承并使用不同的(keywoard)参数super和kwargs可以解决您的问题。但是超级继承和多重继承是复杂的,特别是如果您有比这里更多的继承层。有时甚至没有定义函数的调用顺序(然后python应该抛出一个错误,参见例如mro算法更改的解释)。mixin甚至可能需要一个super().__init__()调用,尽管它们甚至不从任何类继承。总之,如果使用多重继承,代码会变得非常复杂,因此如果您不真正需要它,最好考虑其他方法来建模问题。


这应该管用

1
2
3
4
5
6
class Employee:
    def __init__(self, firstname='', lastname='', age='', dob=''):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age
        self.dob = dob

那你要上儿童班了

1
2
3
4
5
6
7
class Hourly2(Employee):
    def __init__(self,*args, **kwargs):
        super(Hourly2,self).__init__(*args, **kwargs)

    def get_rate(self,rate=None):
    self.data=rate
    print ('My hourly rate is {}'.format(self.data))

现在,让我们举个例子

1
bob = Hourly2('Bob', 'Bar', '23', '12/1/2019')

我们可以检查属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
dir(bob)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'age',
 'dob',
 'firstname',
 'get_rate',
 'lastname']

终于

1
2
bob.age
'23'

1
2
bob.get_rate('$12')
My hourly rate is $12