写在前面
我们在使用 Python 进行面向对象编程时可能会遇到一个函数super()
,那么它是什么?在类继承中它又起到了什么作用呢?今天这篇文章将带你走近Python的super()函数。
什么是super()函数?
Python 中的super()
函数允许子类将任务委托给它的父类或祖先树中的某个类。使用语法是super().method_name(args)
。
下面通过两段代码来展示单继承中如何使用super()。
案例1
class ClassOne:
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
def add_vars(self):
return self.var1 + self.var2
class ClassTwo(ClassOne):
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
在这里,ClassTwo 继承自 ClassOne。现在让我们创建一个ClassTwo的对象。
由于它继承自ClassOne,如果我们调用ctwo.add_vars(),会发生什么?
two = ClassTwo(3,4)
# 会发什么什么呢?
two.add_vars()
如果你认为结果会导致错误,那么你是正确的。它会返回AttributeError: 'ClassTwo' object has no attribute 'var1'
。这里没有设置var1
和var2
的值,因为你从未初始化 ClassOne。
案例2
class ClassOne:
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
def add_vars(self):
return self.var1 + self.var2
class ClassTwo(ClassOne):
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
# 我们增加了这行代码
super().__init__(v1, v2)
在这个案例中,如果你创建一个名为ctwo = ClassTwo(3,4)的ClassTwo对象,然后调用ctwo.add_vars(),它将返回7。这就是super()函数的简单用法。它会委托给实例祖先树中的函数。
two = ClassTwo(3,4)
two.add_vars()
结果:
7
注意!!!
如果你将
super().__init__(v1,v2)
修改为ClassOne.__init__(self,v1,v2)
,它也会起到同样的作用。但是调用
super()
更好,因为你不需要指定委托类的名称。它是一个计算得出的间接引用。如果明天有人更改了被委托类的名称,你的代码仍然可以正常工作。
多重继承
在多重继承的情况下,处理super()
会有点麻烦,而方法解析顺序__mro__
的作用就显现出来了。
考虑下面的例子,我们有一个类 Three 继承自两个类 One 和 Two。
class One:
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
def add_var(self):
return self.var1 + self.var2
class Two:
def __init__(self, var3, var4):
self.var3 = var3
self.var4 = var4
def add_var_2(self):
return self.var3 + self.var4
class Three(One,Two):
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
super().__init__(v1, v2)
现在假设我们创建一个名为three = Three(3,4)
的 Three 类的对象。如果我们调用three.add_var()
和three.add_var_2()
会发生什么?第一个调用会返回7
,而第二个调用会返回AttributeError: 'Three' object has no attribute 'var3'
。
这是因为 Three 的__mro__
是[Three, One, Two],你可以使用print(Three.__mro__)
命令打印出来。通常,MRO 是使用 C3 算法计算的。
那么我们如何让three.add_var_2()
成功调用呢?
我最初考虑的是调用两次super()
,但实际上并不起作用。它只会按照 MRO 的顺序到达 Class One ,然后停止。
为此我想到了上一章的问题,我们可以显式的初始化每一个父类,如下所示。
class One:
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
def add_var(self):
return self.var1 + self.var2
class Two:
def __init__(self, var3, var4):
self.var3 = var3
self.var4 = var4
def add_var_(self):
return self.var3 + self.var4
class Three(One,Two):
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
One.__init__(self, v1, v2)
Two.__init__(self, v1, v2)
现在,如果你使用three = Three(3,4)
创建一个对象,然后调用three.add_var()
或three.add_var_2()
,它每次都会返回7
,而且没有错误。
当我对我发现的解决方案感到高兴时,我在 stackoverflow 上找到了一个帖子。它说父类也应该调用super()
,以确保与子类协同工作。
在父类中使用super()
的代码如下所示:
class One:
def __init__(self, var1, var2):
self.var1 = var1
self.var2 = var2
# 增加的代码
super().__init__(var1, var2)
def add_var(self):
return self.var1 + self.var2
class Two:
def __init__(self, var3, var4):
self.var3 = var3
self.var4 = var4
# 增加的代码
super().__init__()
def add_var_(self):
return self.var3 + self.var4
class Three(One,Two):
def __init__(self, v1, v2):
self.v1 = v1
self.v2 = v2
# 修改的部分
super().__init__(v1,v2)
在类 One 中,我们必须通过super().__init__
传递变量,而在类 Two 中则不必。这是因为 MRO(方法解析顺序)是[Three, One, Two]
,所以 One 的__init__
调用了 Two 的__init__
,而 Two 的__init__
调用了所有类的根类,它不需要任何变量。此外,如果你传递了变量,会导致错误。
这意味着,如果你想以协同类的方式调用super()
函数,我们必须了解它在子类中的使用方式(至少在上面的例子中是如此)。
写在最后
总之,作为开发人员,必须了解这两种使用方式。根据实际需求选用ParentClass.function_name(self,*(args))
或者使用super().function_name(*(args))
来调用父函数,并根据其优点选择其中之一。
希望这个解答对你有所帮助!如果你还有其他问题,请随时提问。
原文始发于微信公众号(harvey的网络日志):Python中的super()如何使用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/231107.html