注: Python 2.7.x 环境下

今晚搜东西无意中看到这篇Understanding Python super() with __init__() methods.

其实这篇老早就看过了, 不过有一篇很好的回答之前没有注意到.

首先说下super(), 我只在类的单继承时的__init__()中使用过.

注意super只能用在新式类(new-style class)中, 也就是继承自object类对象的子类:

  1. class A(object):
  2. ....

以前遇到过一个问题, 排查了半天, 才发现是老式类定义.

传统的super使用方法如:

  1. class Base(object):
  2. def __init__(self, id):
  3. self.id = id
  4. class Child(Base):
  5. def __init__(self, id, name):
  6. super(Child, self).__init__(id)
  7. self.name = name

这个是Python2.2之后才支持的特性, 在之前只能:

  1. class Child(Base):
  2. def __init__(self, id, name):
  3. Base.__init__(self, id)
  4. self.name = name

这样做的好处就是不需要显示的在初始化时指明Child的父类名是什么, 在复杂的继承环境下, 以致会牵一发动一身.

不过就像那篇帖子top1的回答里所说:

But the main advantage comes with multiple inheritance

super在多继承这种更复杂的环境下, 才能发挥真正的威力, 这也是python文档中提到的第二个使用场景. 当然至今没遇到过这种复杂环境, 所以没有发言权.


上面扯了一些super的基本情况, 接着该扯下帖子里top2的回答了.

里面提到了这个用法:

  1. super(self.__class__, self).__init__()

关于__class__:

  1. instance.__class__ : The class to which a class instance belongs.

因为前阵子在使用多线程(threading.Thread)时, 写了一个基类, 然后有两个类分别继承自这个基类, 设置线程名就是类名, 这时就用到了__class__:

  1. class base_thread(threading.Thread):
  2. def __init__(self, **kwargs):
  3. threading.Thread.__init__(self)
  4. self.name = self.__class__.__name__

所以对这个比较敏感, 才留意了下这个回答, 没想到却发现了一些坑...

按照帖子里的那个回复:

This unfortunately does not necessarily work if you want to inherit the constructor from the superclass.

例子:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. class Polygon(object):
  4. def __init__(self, id):
  5. self.id = id
  6. class Rectangle(Polygon):
  7. def __init__(self, id, width, height):
  8. super(self.__class__, self).__init__(id)
  9. self.shape = (width, height)
  10. class Square(Rectangle):
  11. pass
  12. p = Polygon(10)
  13. print p.id
  14. r = Rectangle(5, 1, 2)
  15. print r.id
  16. s = Square(20, 2, 4)
  17. print s.id

运行结果:

  1. % python test.py
  2. 10
  3. 5
  4. Traceback (most recent call last):
  5. File "test.py", line 65, in <module>
  6. s = Square(20, 2, 4)
  7. File "test.py", line 53, in __init__
  8. super(self.__class__, self).__init__(id)
  9. TypeError: __init__() takes exactly 4 arguments (2 given)

执行到Square类时, 报错说应该有4个参数, 但是实际上只有两个.

简化下代码, 并加一些调试输出:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. class Polygon(object):
  4. def __init__(self, id):
  5. print('in Polygon, self.__class__ is %s' % self. 大专栏  扯下Pythonsuper()__class__)
  6. self.id = id
  7. class Rectangle(Polygon):
  8. def __init__(self, id, width, height):
  9. super(self.__class__, self).__init__(id)
  10. #super(Rectangle, self).__init__(id)
  11. print('in Rectangle, self.__class__ is %s' % self.__class__)
  12. self.shape = (width, height)
  13. p = Polygon(10)
  14. print p.id
  15. r = Rectangle(5, 1, 2)
  16. print r.id

结果是:

  1. % python test.py
  2. in Polygon, self.__class__ is <class '__main__.Polygon'>
  3. 10
  4. in Polygon, self.__class__ is <class '__main__.Rectangle'>
  5. in Rectangle, self.__class__ is <class '__main__.Rectangle'>
  6. 5

可以看出来, 在Rectangle初始化时, 通过super调用父类Polygon进行初始化, 而 __class__还是Rectangle.

所以在上一个例子中, Square因为和Rectangle的初始化方法一样, 所以初始化时会调用:

  1. super(Square, self).__init__(id)

即:

  1. Rectangle.__init__(id)

但是实际上Rectangle接收4个参数的初始化, 所以这里报错.

接着考虑, 解决参数个数不一致的问题? 那么就让参数多一致:

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. class Polygon(object):
  4. def __init__(self, id, width, weight):
  5. print('in Polygon, self.__class__ is %s' % self.__class__)
  6. self.id = id
  7. class Rectangle(Polygon):
  8. def __init__(self, id, width, height):
  9. super(self.__class__, self).__init__(id, width, height)
  10. #super(Rectangle, self).__init__(id)
  11. print('in Rectangle, self.__class__ is %s' % self.__class__)
  12. self.shape = (width, height)
  13. class Square(Rectangle):
  14. def __init__(self, id, width, height):
  15. super(self.__class__, self).__init__(id, width, height)
  16. #super(Rectangle, self).__init__(id)
  17. print('in Square, self.__class__ is %s' % self.__class__)
  18. self.shape = (width, height)
  19. p = Polygon(10, 3, 6)
  20. print p.id
  21. r = Rectangle(5, 1, 2)
  22. print r.id
  23. s = Square(20, 2, 4)
  24. print s.id

运行报错:

  1. % python test.py
  2. in Polygon, self.__class__ is <class '__main__.Polygon'>
  3. 10
  4. in Polygon, self.__class__ is <class '__main__.Rectangle'>
  5. in Rectangle, self.__class__ is <class '__main__.Rectangle'>
  6. 5
  7. Traceback (most recent call last):
  8. File "test.py", line 30, in <module>
  9. s = Square(20, 2, 4)
  10. File "test.py", line 18, in __init__
  11. super(self.__class__, self).__init__(id, width, height)
  12. File "test.py", line 11, in __init__
  13. super(self.__class__, self).__init__(id, width, height)
  14. File "test.py", line 11, in __init__
  15. ...
  16. File "test.py", line 11, in __init__
  17. super(self.__class__, self).__init__(id, width, height)
  18. File "test.py", line 11, in __init__
  19. super(self.__class__, self).__init__(id, width, height)
  20. File "test.py", line 11, in __init__
  21. super(self.__class__, self).__init__(id, width, height)
  22. RuntimeError: maximum recursion depth exceeded while calling a Python object

在Rectangle的super这一样发生了无限循环.

在Square的super函数里:

  1. super(self.__class__, self).__init__(id, width, height)

相当于:

  1. Rectangle.__init__(id, width, height)

而此时在Retangle的super函数里, __class__还是等于Square, 所以super(self.__class__, self)就是Rectangle自身, 所以在这里发生了死循环.

唯一的做法就是在Square中重定义__init__, 并且和__class__无关.

这块有点绕, 需要理解下.

扯下Python的super()的更多相关文章

  1. Linux环境下Python的安装过程

    Linux环境下Python的安装过程 前言 一般情况下,Linux都会预装 Python了,但是这个预装的Python版本一般都非常低,很多 Python的新特性都没有,必须重新安装新一点的版本,从 ...

  2. 由Python的super()函数想到的

    python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...

  3. Python: 你不知道的 super

    https://segmentfault.com/a/1190000007426467 Python: 你不知道的 super 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我 ...

  4. python中super的理解(转)

    原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...

  5. Python面试题之Python的Super方法

    我们最常见的,可以说几乎唯一能见到的使用super的形式是: class SubClass(BaseClass): def method(self): super(SubClass, self).me ...

  6. Python’s super() considered super!

    如果你没有被Python的super()惊愕过,那么要么是你不了解它的威力,要么就是你不知道如何高效地使用它. 有许多介绍super()的文章,这一篇与其它文章的不同之处在于: 提供了实例 阐述了它的 ...

  7. python的super深入了解(转)

    1.python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init__(s ...

  8. Python中super的用法【转载】

    Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143  收藏 展开 转载自 Python面向对象中super用法与MRO ...

  9. 算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!

    算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!

随机推荐

  1. Linux Shell编程case语句

    http://blog.csdn.net/dreamtdp/article/details/8048720 case语句适用于需要进行多重分支的应用情况. case分支语句的格式如下: case $变 ...

  2. 【STM32H7教程】第50章 STM32H7的LCD控制器LTDC基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第50章       STM32H7的LCD控制器LTDC基础 ...

  3. SASS - 输出格式

    SASS – 简介 SASS – 环境搭建 SASS – 使用Sass程序 SASS – 语法 SASS – 变量 SASS- 局部文件(Partial) SASS – 混合(Mixin) SASS ...

  4. 常用模块-正则re

    常用模块之正则模块 """ 正则表达式与re模块的关系 1.正则表达式是一门独立的技术,任何语言均可使用 2.python中要想使用正则表达式需要通过re模块 " ...

  5. 用ps画一个Gif的小房子(1)

    效果如图: 制作方法: 1.新建200*200的画布:复制一块小房子图片 2.点击窗口-时间轴-勾选帧动画 3.如图所示(我这边是一帧对应一个图层) 4.新建图层-这边要新建24个图层,每个图层对应不 ...

  6. 读书笔记 - javascript 高级程序设计 - 第一章 简介

      第一章 简介   诞生时间 1995 最初用途 客服端验证 第一版标准 注意是标准 1997年 Ecma-262  一个完整的js实现由三部分组成 ECMAScript DOM 文档对象模型 BO ...

  7. Vue-router(5)之 路由的before家族

    beforeEach方法 import Vue from 'vue' import Router from 'vue-router' import Son1 from '@/view/New/son1 ...

  8. 四、python杂项

    一.pycharm单行和多行注释快捷键                        多行注释就一个组合键:选中+Ctrl+/

  9. PAT Advanced 1115 Counting Nodes in a BST (30) [⼆叉树的遍历,BFS,DFS]

    题目 A Binary Search Tree (BST) is recursively defined as a binary tree which has the following proper ...

  10. Pmw大控件(二)

    Pmw大控件英文名Pmw Python megawidgets 官方参考文档:Pmw 1.3 Python megawidgets 一,如何使用Pmw大控件 下面以创建一个计数器(Counter)为例 ...