扯下Python的super()
注: Python 2.7.x 环境下
今晚搜东西无意中看到这篇Understanding Python super() with __init__()
methods.
其实这篇老早就看过了, 不过有一篇很好的回答之前没有注意到.
首先说下super()
, 我只在类的单继承时的__init__()
中使用过.
注意super只能用在新式类(new-style class)中, 也就是继承自object类对象的子类:
class A(object):
....
以前遇到过一个问题, 排查了半天, 才发现是老式类定义.
传统的super使用方法如:
class Base(object):
def __init__(self, id):
self.id = id
class Child(Base):
def __init__(self, id, name):
super(Child, self).__init__(id)
self.name = name
这个是Python2.2之后才支持的特性, 在之前只能:
class Child(Base):
def __init__(self, id, name):
Base.__init__(self, id)
self.name = name
这样做的好处就是不需要显示的在初始化时指明Child的父类名是什么, 在复杂的继承环境下, 以致会牵一发动一身.
不过就像那篇帖子top1的回答里所说:
But the main advantage comes with multiple inheritance
super在多继承这种更复杂的环境下, 才能发挥真正的威力, 这也是python文档中提到的第二个使用场景. 当然至今没遇到过这种复杂环境, 所以没有发言权.
上面扯了一些super的基本情况, 接着该扯下帖子里top2的回答了.
里面提到了这个用法:
super(self.__class__, self).__init__()
关于__class__
:
instance.__class__ : The class to which a class instance belongs.
因为前阵子在使用多线程(threading.Thread
)时, 写了一个基类, 然后有两个类分别继承自这个基类, 设置线程名就是类名, 这时就用到了__class__
:
class base_thread(threading.Thread):
def __init__(self, **kwargs):
threading.Thread.__init__(self)
self.name = self.__class__.__name__
所以对这个比较敏感, 才留意了下这个回答, 没想到却发现了一些坑...
按照帖子里的那个回复:
This unfortunately does not necessarily work if you want to inherit the constructor from the superclass.
例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
p = Polygon(10)
print p.id
r = Rectangle(5, 1, 2)
print r.id
s = Square(20, 2, 4)
print s.id
运行结果:
% python test.py
10
5
Traceback (most recent call last):
File "test.py", line 65, in <module>
s = Square(20, 2, 4)
File "test.py", line 53, in __init__
super(self.__class__, self).__init__(id)
TypeError: __init__() takes exactly 4 arguments (2 given)
执行到Square类时, 报错说应该有4个参数, 但是实际上只有两个.
简化下代码, 并加一些调试输出:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Polygon(object):
def __init__(self, id):
print('in Polygon, self.__class__ is %s' % self. 大专栏 扯下Python的super()__class__)
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
#super(Rectangle, self).__init__(id)
print('in Rectangle, self.__class__ is %s' % self.__class__)
self.shape = (width, height)
p = Polygon(10)
print p.id
r = Rectangle(5, 1, 2)
print r.id
结果是:
% python test.py
in Polygon, self.__class__ is <class '__main__.Polygon'>
10
in Polygon, self.__class__ is <class '__main__.Rectangle'>
in Rectangle, self.__class__ is <class '__main__.Rectangle'>
5
可以看出来, 在Rectangle初始化时, 通过super调用父类Polygon进行初始化, 而 __class__
还是Rectangle.
所以在上一个例子中, Square因为和Rectangle的初始化方法一样, 所以初始化时会调用:
super(Square, self).__init__(id)
即:
Rectangle.__init__(id)
但是实际上Rectangle接收4个参数的初始化, 所以这里报错.
接着考虑, 解决参数个数不一致的问题? 那么就让参数多一致:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Polygon(object):
def __init__(self, id, width, weight):
print('in Polygon, self.__class__ is %s' % self.__class__)
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id, width, height)
#super(Rectangle, self).__init__(id)
print('in Rectangle, self.__class__ is %s' % self.__class__)
self.shape = (width, height)
class Square(Rectangle):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id, width, height)
#super(Rectangle, self).__init__(id)
print('in Square, self.__class__ is %s' % self.__class__)
self.shape = (width, height)
p = Polygon(10, 3, 6)
print p.id
r = Rectangle(5, 1, 2)
print r.id
s = Square(20, 2, 4)
print s.id
运行报错:
% python test.py
in Polygon, self.__class__ is <class '__main__.Polygon'>
10
in Polygon, self.__class__ is <class '__main__.Rectangle'>
in Rectangle, self.__class__ is <class '__main__.Rectangle'>
5
Traceback (most recent call last):
File "test.py", line 30, in <module>
s = Square(20, 2, 4)
File "test.py", line 18, in __init__
super(self.__class__, self).__init__(id, width, height)
File "test.py", line 11, in __init__
super(self.__class__, self).__init__(id, width, height)
File "test.py", line 11, in __init__
...
File "test.py", line 11, in __init__
super(self.__class__, self).__init__(id, width, height)
File "test.py", line 11, in __init__
super(self.__class__, self).__init__(id, width, height)
File "test.py", line 11, in __init__
super(self.__class__, self).__init__(id, width, height)
RuntimeError: maximum recursion depth exceeded while calling a Python object
在Rectangle的super这一样发生了无限循环.
在Square的super函数里:
super(self.__class__, self).__init__(id, width, height)
相当于:
Rectangle.__init__(id, width, height)
而此时在Retangle的super函数里, __class__
还是等于Square, 所以super(self.__class__, self)
就是Rectangle自身, 所以在这里发生了死循环.
唯一的做法就是在Square中重定义__init__
, 并且和__class__
无关.
这块有点绕, 需要理解下.
扯下Python的super()的更多相关文章
- Linux环境下Python的安装过程
Linux环境下Python的安装过程 前言 一般情况下,Linux都会预装 Python了,但是这个预装的Python版本一般都非常低,很多 Python的新特性都没有,必须重新安装新一点的版本,从 ...
- 由Python的super()函数想到的
python-super *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !im ...
- Python: 你不知道的 super
https://segmentfault.com/a/1190000007426467 Python: 你不知道的 super 在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我 ...
- python中super的理解(转)
原文地址:https://www.zhihu.com/question/20040039 针对你的问题,答案是可以,并没有区别.但是这题下的回答我感觉都不够好. 要谈论 super,首先我们应该无视 ...
- Python面试题之Python的Super方法
我们最常见的,可以说几乎唯一能见到的使用super的形式是: class SubClass(BaseClass): def method(self): super(SubClass, self).me ...
- Python’s super() considered super!
如果你没有被Python的super()惊愕过,那么要么是你不了解它的威力,要么就是你不知道如何高效地使用它. 有许多介绍super()的文章,这一篇与其它文章的不同之处在于: 提供了实例 阐述了它的 ...
- python的super深入了解(转)
1.python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init__(s ...
- Python中super的用法【转载】
Python中super的用法[转载] 转载dxk_093812 最后发布于2019-02-17 20:12:18 阅读数 1143 收藏 展开 转载自 Python面向对象中super用法与MRO ...
- 算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!
算是休息了这么长时间吧!准备学习下python文本处理了,哪位大大有好书推荐的说下!
随机推荐
- Ubuntu 14.04 搭建 ftp
一.安装ftp服务器vsftpd $sudo apt-get update $sudo apt-get install vsftpd ftp服务器使用21端口,安装成功之后查看是否打开21端口 $ s ...
- POJ 1068:Parencodings
Parencodings Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 22849 Accepted: 13394 De ...
- 如何在Ubuntu 18.04上安装和卸载TeamViewer
卸载命令:sudo apt --purge remove teamviewer 安装:https://www.linuxidc.com/Linux/2018-05/152282.htm 如何在Ubun ...
- 10几行代码,用python打造实时截图识别OCR
你一定用过那种“OCR神器”,可以把图片中的文字提取出来,极大的提高工作效率. ! 今天,我们就来做一款实时截图识别的小工具.顾名思义,运行程序时,可以实时的把你截出来的图片中的文字识别出来. 下 ...
- leetcode406 ,131,1091 python
LeetCode 406. Queue Reconstruction by Height 解题报告题目描述Suppose you have a random list of people standi ...
- 提升Python编程效率的几种方法
前言 我们知道Python这门语言在运行速度上已经败给了许多别的语言(比如C, C++, Java, Golang....).但从一个开发者的角度来看Python是我最喜欢的语言,很大一部分原因在于其 ...
- 移动端H5开发遇到的问题及解决方法
本篇文章给大家带来的内容是关于移动端H5开发遇到的问题及解决方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 微信分享签名错误invalid signature vue单页应用hi ...
- import torch 报错
1.进入官网 https://pytorch.org/ 2.复制command到anaconda环境,即可
- java笔记-手写
- VUE,index key v-for
列表渲染语法 v-forv-for 循环对象 <article v-for="(item, key, index) of info">{{item}} {{key}} ...