python - 调用 super().__init__(**kwargs) 和多重继承?

标签 python python-3.x multiple-inheritance super

我正在尝试学习和理解如何在 Python 中使用 super,我一直在关注“从新手到专家的 Python 之旅”一书,尽管我觉得我理解这个概念,但我在自己的代码中执行 super 时遇到了问题。

例如,这个方法对我有用:

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 "{} {}\nAge: {}\nSex: {}\nDOB: {}\n".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())

返回以下内容:

Bob Foo
Age: 23
Sex: M
DOB: 12/1/1980

The hourly rate of Bob is $15 
None

这是我所期望的(我不确定为什么也返回“无”,也许有人可以解释一下?)。

然后我想尝试使用 super 但使用 **kwargs 像这样:

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 "{} {}\nAge: {}\nSex: {}".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')

返回此错误:

  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 特有的,而是 kwargs 特有的。如果我们丢弃大部分代码并删除 super,它看起来像这样:

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 有用。假设我们用一些很好的辅助方法围绕子进程编写了一些包装器(架构可能不是最适合这个问题的,但是只看到具有继承的动物也不是很有帮助。多重继承是一种非常罕见的情况,所以很难想出不是 Animals、GameEntities 或 GUIwidgets 的好例子):

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 - 我们可以只显式使用父类(super class)的名称并获得我们想要的行为。我们可以在这里重写以使用 super 但它不会改变行为。如果你只继承一个类,你并不严格需要 super,尽管它可以帮助你避免重复你继承的类名。让我们添加到我们的类层次结构中,并包括从多个类继承:

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)

如果我们跟踪 DownloadAndCheck 的初始化,我们会看到 Process.__init__ 被调用了两次,一次是通过 DownloadExecutableBefore.__init__ ,一次是通过 AuthenticationCheckerProcess.__init__!所以我们要包装的进程也运行了两次,这不是我们想要的。在这个例子中,我们可以通过不在进程的初始化中调用 self.run() 来轻松解决这个问题,但在现实世界的情况下,这并不总是像这里这样容易解决。在这种情况下,调用 Process.__init__ 似乎是错误的。我们能以某种方式解决这个问题吗?

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__ 忽略键盘:

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) 调用将成功,每个初始化程序只接受它理解的关键字,并简单地将其他关键字传递到更下方。

因此,如果您有多重继承并使用不同的(关键字)参数,super 和 kwargs 可以解决您的问题。但是超继承和多重继承是复杂的,特别是如果你有比这里更多的继承层。有时函数被调用的顺序甚至没有定义(然后 python 应该抛出错误,参见例如 explenation of change of MRO algorithm )。 Mixin 甚至可能需要 super().__init__() 调用,尽管它们甚至不从任何类继承。总而言之,如果您使用多重继承,您的代码会变得非常复杂,因此如果您真的不需要它,通常最好考虑其他方法来为您的问题建模。

关于python - 调用 super().__init__(**kwargs) 和多重继承?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50550153/

相关文章:

c++ - 由于多重继承的重复变量

python - 我的代码未按 python 3 中的 try 和 except 函数的预期运行

linux - 在 python 中等效的 openssl 命令?

Python正则表达式查找某个字符串后大括号内的所有内容

python - 如何使用具有多重继承的数据类特殊方法?

c++ - 纯虚函数和多重继承

Python 日期时间增量 - 15 分钟?

python - 以 nohup 启动的进程未与父进程分离

Python仅导入包内的模块

python-3.x - pygatt : Unable to execute device. 订阅()