Python ---- super()

我们经常在类的继承当中使用super(), 来调用父类中的方法。例如下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
    def func(self):
        print('OldBoy')
 
 
class B(A):
    def func(self):
        super().func()
        print('LuffyCity')
 
 
A().func()
B().func()

输出的结果为:

OldBoy
OldBoy
LuffyCity

A实例化的对象调用了func方法,打印输出了 Oldboy;

B实例化的对象调用了自己的func方法,先调用了父类的方法打印输出了 OldBoy ,再打印输出 LuffyCity 。

这样是Python3的写法,今天咱们也只讨论Python3中的super。

如果不使用super的话,想得到相同的输出截个,还可以这样写B的类:

1
2
3
4
class B(A):
    def func(self):
        A.func(self)
        print('LuffyCity')

这样能实现相同的效果,只不过传了一个self参数。那为什么还要使用super()呢?

那我看看有这样的一个继承关系的类(钻石继承):

1
2
3
4
5
6
7
  Base
  /  \
 /    \
A      B
 \    /
  \  /
   C

代码是这样的:

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
class Base:
    def __init__(self):
        print('Base.__init__')
 
 
class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')
 
 
class B(Base):
    def __init__(self):
        Base.__init__(self)
        print('B.__init__')
 
 
class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('C.__init__')
 
 
C()

输出的结果是:

Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__

每个子类都调用父类的__init__方法,想把所有的初始化操作都做一遍,但是出现了一个问题,Base类的__init__方法被调用了两次,这是多余的操作,也是不合理的。

那我们改写成使用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
class Base:
    def __init__(self):
        print('Base.__init__')
 
 
class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')
 
 
class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')
 
 
class C(A, B):
    def __init__(self):
        super().__init__()
        print('C.__init__')
 
 
C()

输出的结果是:

Base.__init__
B.__init__
A.__init__
C.__init__ 

这样执行的结果就比较满意,是大多数人想要的结果。那为什么会是这样的结果呢?

那是因为我们每定义一个类的时候,Python都会创建一个MRO列表,用来管理类的继承顺序。

1
2
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

Python通过这个列表从左到右,查找继承的信息。Python3中的类都是新式类,都有这个mro属性,能看出来是广度优先的查找原则。经典类就没有mro属性,但它的查找原则是深度优先。

那我回到super的问题上来,让我们先看看super的官方定义

super([type[, object-or-type]])

返回一个代理对象,该对象将方法调用委托给类的父类或兄弟类。这对于访问类中已重写的继承方法非常有用。搜索顺序与getattr()使用的搜索顺序相同,只是类型本身被跳过。

类的__mro__属性列出了getattr()和super()使用的方法解析搜索顺序。属性是动态的,可以在继承层次结构更新时进行更改。

看到官方的解释就可以很清楚的明白,super是一个类,实例化之后得到的是一个代理的对象,而不是得到了父类,并且我们使用这个代理对象来调用父类或者兄弟类的方法。

那我们再看看super的使用方法:

super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)

super至少需要一个参数,并且类型需要是类。

不传参数的会报错。只传一个参数的话是一个不绑定的对象,不绑定的话也就没什么用了。

1
2
print(super(C))
print(super())

输出结果:

RuntimeError: super(): no arguments
<super: <class 'C'>, NULL>

在定义类当中可以不写参数,Python会自动根据情况将两个参数传递给super。

1
2
3
4
5
6
7
8
class C(A, B):
    def __init__(self):
        print(super())
        super().__init__()
        print('C.__init__')
 
 
C()

输出结果:

<super: <class 'C'>, <C object>>
Base.__init__
B.__init__
A.__init__
C.__init__

所以我们在类中使用super的时候参数是可以省略的。

第三种用法, super(type, obj) 传递一个类和对象,得到的是一个绑定的super对象。这还需要obj是type的实例,可以不是直接的实例,是子类的实例也行。

1
2
3
= A()
print(super(A, a))
print(super(Base, a))

输出结果:

Base.__init__
A.__init__
<super: <class 'A'>, <A object>>
<super: <class 'Base'>, <A object>>

第三种用法, super(type, type2)传递两个类,得到的也是一个绑定的super对象。这需要type2是type的子类。

1
2
3
print(super(Base, A))
print(super(Base, B))
print(super(Base, C))

输出结果:

<super: <class 'Base'>, <A object>>
<super: <class 'Base'>, <B object>>
<super: <class 'Base'>, <C object>>

接下来我们就该说说查找顺序了,两个参数,是按照那个参数去计算MRO呢?

我们将C类中的super的参数填写上,并且实例化,看看输出的结果。

1
2
3
4
class C(A, B):
    def __init__(self):
        super(C, self).__init__()
        print('C.__init__')

输出结果:

Base.__init__
B.__init__
A.__init__
C.__init__

看结果和之前super没填参数的结果是一样的。

那我们将super的第一个参数改为A:

1
2
3
4
class C(A, B):
    def __init__(self):
        super(A, self).__init__()
        print('C.__init__')

输出结果:

Base.__init__
B.__init__
C.__init__

咦!?那A.__init__怎么跑丢了呢?多出来了B.__init__呢?

这是应为Python是按照第二个参数来计算MRO,这次的参数是self,也就是C的MRO。在这个顺序中跳过一个参数(A)找后面一个类(B),执行他的方法。

知道这个后,输出的结果就可以理解了。 super(A, self).__init__() 没有执行Base的方法,而是执行了B的方法。


那我们接下来说说 super(type, obj)  和 super(type, type2)的区别。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Base:
    def func(self):
        return 'from Base'
 
 
class A(Base):
    def func(self):
        return 'from A'
 
 
class B(Base):
    def func(self):
        return 'from B'
 
 
class C(A, B):
    def func(self):
        return 'from C'
 
 
c_obj = C()
 
print(super(C, C))
print(super(C, c_obj))

输出结果:

<super: <class 'C'>, <C object>>
<super: <class 'C'>, <C object>>

两次的打印结果一模一样,verygood。那他们的方法是否是一样的呢?测试一下。

1
2
print(super(C, C).func is super(C, c_obj).func)
print(super(C, C).func == super(C, c_obj).func)

输出结果:

False
False

他俩的方法既不是指向同一个,值还不相等。是不是搞错了呢?再试试下面的看看。

1
2
3
4
5
6
c1 = super(C, C)
c2 = super(C, C)
print(c1 is c2)
print(c1 == c2)
print(c1.func is c2.func)
print(c1.func == c2.func)

输出结果:

False
False
True
True

c1和c2不是一个对象,但是他们的方法却是相同的。

那 super(C, C).func 和 super(C, c_obj).func 的确是不同的。那打印出来看看有什么区别:

1
2
print(super(C, C).func)
print(super(C, c_obj).func)

输出结果:

<function A.func at 0x0000000009F4D6A8>
<bound method A.func of <__main__.C object at 0x00000000022A94E0>>

super的第二个参数传递的是类,得到的是函数。

super的第二个参数传递的是对象,得到的是绑定方法。

函数和绑定方法的区别就不再赘述了,在这里想得到一样的结果,只需要给函数传递一个参数,而绑定方法则不需要传递额外的参数了。

1
2
print(super(C, C).func(c_obj))
print(super(C, c_obj).func())

输出结果:

from A
from A

那我现在总结一下:

  1. super()使用的时候需要传递两个参数,在类中可以省略不写,我们使用super()来找父类或者兄弟类的方法;
  2. super()是根据第二个参数来计算MRO,根据顺序查找第一个参数类后的方法。
  3. super()第二个参数是类,得到的方法是函数,使用时要传self参数。第二个参数是对象,得到的是绑定方法,不需要再传self参数。

给使用super()的一些建议:

  1. super()调用的方法要存在;
  2. 传递参数的时候,尽量使用*args 与**kwargs;
  3. 父类中的一些特性,比如【】、重写了__getattr__,super对象是不能使用的。
  4. super()第二个参数传的是类的时候,建议调用父类的类方法和静态方法。

Python ---- super()使用的更多相关文章

  1. Python super继承详解

    MRO(Method resolution order)是python用来解析方法调用顺序的,mro中记录了一个类的所有基类的类类型序列,super不是简单地调用基类的方法,而是按照MRO中的顺序来调 ...

  2. python super

    http://hi.baidu.com/thinkinginlamp/item/3095e2f52c642516ce9f32d5 Python中对象方法的定义很怪异,第一个参数一般都命名为self(相 ...

  3. Python super使用

    一 基础使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如: #!/usr ...

  4. python super参数错误

    # -*- coding:utf-8 _*-"""@author:Administrator@file: yamlparser.py@time: 2018/09/07&q ...

  5. python super超类方法

    super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重复调用( ...

  6. Python super() 函数的概念和例子

    概念: super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重 ...

  7. python: super原理

    super() 的入门使用 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如: ...

  8. python super()函数

    super()函数是用来调用父类(超类)的一个方法 super()的语法: python 2 的用法: super(Class, self).xxx  # class是子类的名称 class A(ob ...

  9. (转)Python: super 没那么简单

    原文:https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html python 约定¶ 单继承¶ 多 ...

随机推荐

  1. Sapnco3 RfcTable Structure

    RfcTable 中字段 并不固定,以下内容仅供参考 1. 包含IDOC的 RfcTable SDATA字段值为IDOC数据,解析IDOC数据需依据IDOC字段长度对SDATA进行截取 functio ...

  2. springmvc中拦截器与springmvc全局异常处理器的问题

    最近在做一个练手的小项目, 系统架构中用了springmvc的全局异常处理器, 做了系统的统一异常处理. 后来加入了springmvc的拦截器, 为了一些需求, 在拦截器中的 preHandle 方法 ...

  3. 两种方式创建Maven项目【方式二】

    1.不勾选Create a simple project,直接点击下一步 2.选择maven-archetype-webapp下一步 3.填写相关信息,点击下一步完成 4.此时会报一个jsp的错误.我 ...

  4. 两种方式创建Maven项目【方式一】

    经常使用maven进行项目的管理,今天整理两种方式创建maven项目及创建过程中碰到的问题怎么解决: 方式一: 1.新建maven项目,点击下一步. 2.勾选Create a simple proje ...

  5. javabrideg的使用实践

    (1)进入这个网站http://sourceforge.net/projects/php-java-bridge/files,选择Binary package,然后选择最新的版本Php-java-br ...

  6. Linux 定时任务 crontab 讲解

    linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另 外, 由于使用者自己也可以设置计划任务,所以, ...

  7. Centos7手工安装Kubernetes集群

    安装Kubernetes集群有多种方式,前面介绍了Kubeadm的方式,本文将介绍手工安装的方法. 安装环境有3台Azure上的VM: Hkube01:10.0.1.4 Hkube02:10.0.1. ...

  8. ror中间一些单复数的规则

    基于rails generate生成的东西里面,有一些单复数的规则刚开始很不理解,觉得很复杂,容易弄错,特此记录 model 实际是对于数据库数据的对象化,只体现单个对象,比如模型user有name和 ...

  9. 工具软件集合 Adobe AE PS Pr CC 2018 2019 破解教程

    来源https://mp.weixin.qq.com/s/zeq1sTmaPsKt7Bsok0Ldrg(若链接失效,请关注软件安装管家公众号) 相关链接 Office 2019破解教程 Adobe 2 ...

  10. git的分布式和集中式

    当然,Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面.