1. 版本支持 / 写法差异#

在Python 2.x 中

如果你至今使用的还是 Python 2.x,那么你需要了解一下,在Python 2.x中存在着两种类:经典类和新式类。

什么是经典类?

Copy

Copy

# 不继承自object class Ming: pass 什么是新式类? # 显示继承object class Ming(object): pass

在Python 3.x 中

如果你已经摒弃了Python 2.x,而已经完全投入了Python 3.x的怀抱,那你可能不需要关心,因为在Python3.x中所有的类都是新式类(所有类都(显示/隐式)继承自object)。

有如下三种写法,它们是等价的。

Copy

Copy

class Ming: pass class Ming(): pass class Ming(object): pass

2. 使用方法 / 独特属性#

经典类无法使用super()

经典类的类型是 classobj

新式类的类型是 type,保持class与type的统一。

新式类增加了__slots__内置属性, 可以把实例属性的种类锁定到__slots__规定的范围之中.
新式类增加了__getattribute__方法,在获取属性时可以进入此方法。而经典类不会。

Copy

Copy

# 经典类:py2.7 class Kls01: def __getattribute__(self, *args, **kwargs): print("MING - 01") # 新式类 class Kls02(object): def __getattribute__(self, *args, **kwargs): print("MING - 02") kls02 = Kls02() kls02.name kls01 = Kls01() kls01.name

输出如下

Copy

Copy

MING - 02 Traceback (most recent call last): File "F:/Python Script/Tornado/yang.py", line 13, in <module> kls01.name AttributeError: Kls01 instance has no attribute 'name'

3. MRO 查找算法的演变#

经典类中

经典类,MRO采用的是深度优先算法。

为了验证,这个继承顺序。在 Python2.x 中可以借助自带模块 inspect 来检验。

Copy

Copy

import inspect class A:pass class B(A):pass class C(A):pass class D(B, C):pass print inspect.getmro(D)

输出如下,可以看出,继承顺序是 D -> B -> A -> C

Copy

Copy

(<class __main__.D at 0x0000000005836A08>, <class __main__.B at 0x0000000005836768>, <class __main__.A at 0x00000000058368E8>, <class __main__.C at 0x0000000005836AC8>)

非常好理解,但是在菱形继承时,方法的调用会出现问题。

假设 d 是 D 的一个实例,那么执行 d.show()是调用 A.show() 呢 还是调用 C.show()呢?

在经典类中,由于是深度优先,所以是会选择 A.show()。但是很明显,C.show() 是 A.show() 的更具体化版本(显示了更多的信息),但我们的x.show() 没有调用它,而是调用了 A.show(),这显然是不合理的。所以这才有了后来的一步一步优化。

新式类中

为解决经典类 MRO 所存在的问题,Python 2.2 针对新式类提出了一种新的 MRO 计算方式:在定义类时就计算出该类的 MRO 并将其作为类的属性。

Python 2.2 的新式类 MRO 计算方式和经典类 MRO 的计算方式非常相似:它仍然采用从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个。重新考虑上面「菱形继承」的例子:

同样地,我们也来验证一下。另说明,在新式类中,除用inspect外,可以直接通过__mro__属性获取类的 MRO。

Copy

Copy

import inspect class A(object):pass class B(A):pass class C(A):pass class D(B, C):pass # 或者通过 D.__mro__ 查找 print inspect.getmro(D)

输出如下,可以看出,继承顺序变成了 D -> B -> C -> A

Copy

Copy

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

这下,菱形问题解决了。

再来看一个复杂一点的例子。

如果只依靠上面的算法,我们来一起算下,其继承关系是怎样的。

  1. 首先进行深度遍历:[C, A, X, object, Y, object, B, Y, object, X, object];
  2. 然后,只保留重复元素的最后一个:[C, A, B, Y, X, object]。

同样来验证一下。

Copy

Copy

class X(object): pass class Y(object): pass class A(X, Y): pass class B(Y, X): pass class C(A, B): pass print(C.__mro__)

输出报错,它告诉我们 X,Y 具有二义性的继承关系(这是从Python 2.3后的 C3算法 才有的)。

Copy

Copy

Traceback (most recent call last): File "F:/Python Script/Tornado/yang.py", line 7, in <module> class C(A, B): pass TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution order (MRO) for bases X, Y

具体为什么会这样,我们来看一下。

对于 A 来说,其搜索顺序为[A, X, Y, object];
对于 B,其搜索顺序为 [B, Y, X, object];
对于 C,其搜索顺序为[C, A, B, X, Y, object]。

我们会发现,B 和 C 中 X、Y 的搜索顺序是相反的!也就是说,当 B 被继承时,它本身的行为竟然也发生了改变,这很容易导致不易察觉的错误。此外,即使把 C 搜索顺序中 X 和 Y 互换仍然不能解决问题,这时候它又会和 A 中的搜索顺序相矛盾。

对于复杂一点的继承关系,我们在写代码的时候最好做到心中有数。接下来,就教教你,如何在层层复杂的继承关系中,计算出继承顺序。

例如下面这张图。

计算过程,会采用一种 merge算法。它的基本思想如下:

  1. 检查第一个列表的头元素(如 L[B1] 的头),记作 H。
  2. 若 H 未出现在其它列表的尾部,则将其输出,并将其从所有列表中删除,然后回到步骤1;否则,取出下一个列表的头部记作 H,继续该步骤。
  3. 重复上述步骤,直至列表为空或者不能再找出可以输出的元素。如果是前一种情况,则算法结束;如果是后一种情况,说明无法构建继承关系,Python 会抛出异常。

你可以在草稿纸上,参照上面的merge算法,写出如下过程

Copy

Copy

L[object] = [object] L[D] = [D, object] L[E] = [E, object] L[F] = [F, object] L[B] = [B, D, E, object] L[C] = [C, D, F, object] L[A] = [A] + merge(L[B], L[C], [B], [C]) = [A] + merge([B, D, E, object], [C, D, F, object], [B], [C]) = [A, B] + merge([D, E, object], [C, D, F, object], [C]) = [A, B, C] + merge([D, E, object], [D, F, object]) = [A, B, C, D] + merge([E, object], [F, object]) = [A, B, C, D, E] + merge([object], [F, object]) = [A, B, C, D, E, F] + merge([object], [object]) = [A, B, C, D, E, F, object]

当然,可以用代码验证类的 MRO,上面的例子可以写作:

Copy

Copy

class D(object): pass class E(object): pass class F(object): pass class B(D, E): pass class C(D, F): pass class A(B, C): pass A.__mro__

输出如下

Copy

Copy

(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.

本文首发于python黑洞网,csdn同步更新

Python 中的新式类和经典类的区别?相关推荐

  1. python中的__new__与__init__,新式类和经典类(2.x)

    在python2.x中,从object继承得来的类称为新式类(如class A(object))不从object继承得来的类称为经典类(如class A()) 新式类跟经典类的差别主要是以下几点: 1 ...

  2. python中新式类和经典类

    python中的类分为新式类和经典类,具体有什么区别呢?简单的说, 1.新式类都从object继承,经典类不需要. Python 2.x中默认都是经典类,只有显式继承了object才是新式类 Pyth ...

  3. python新式类和经典类区别_Python中新式类和经典类的区别,钻石继承

    1)首先,写法不一样: class A: pass class B(object): 2)在多继承中,新式类采用广度优先搜索,而旧式类是采用深度优先搜索. 3)新式类更符合OOP编程思想,统一了pyt ...

  4. python中新式类和经典类的区别

    1.新式类和经典类 在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于"新式类",都会获得所有"新式类"的 ...

  5. python单例模式控制成只初始化一次,常规型的python单例模式在新式类和经典类中的区别。...

    单例模式的写法非常多,但常规型的单例模式就是这样写的,各种代码可能略有差异,但核心就是要搞清楚类属性 实例属性,就很容易写出来,原理完全一模一样. 如下: 源码: class A(object):de ...

  6. python中的新式类与旧式类的一些基于descriptor的概念(上)

    python中基于descriptor的一些概念(上) 1. 前言 2. 新式类与经典类 2.1 内置的object对象 2.2 类的方法 2.2.1 静态方法 2.2.2 类方法 2.3 新式类(n ...

  7. python3新式类_Python中新式类与经典类的区别详析

    1.新式类与经典类 在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于"新式类",都会获得所有"新式类"的 ...

  8. python经典类新式类_Python的新式类和经典类

    1. 前言 python在2.2版本中引入了descriptor功能,也正是基于这个功能实现了新式类(new-styel class)的对象模型, 同时解决了之前版本中经典类(classic clas ...

  9. python新式类和经典类的区别

    python新式类和经典类的区别 1,新式类和经典类的区别: 广度优先和深度优先,这主要是在多类继承的时候会使用到,如下多类继承的D类对比: 经典类在加载的时候采用的是深度优先算法,二新式类采用的是广 ...

  10. python新式类好还是经典类_python新式类和经典类的区别?

    python新式类和经典类的区别? 1)首先,写法不一样: class A: pass class B(object): pass 2)在多继承中,新式类采用广度优先搜索,而旧式类是采用深度优先搜索. ...

最新文章

  1. Simulink仿真 第二节 输入输出和子系统
  2. 卡尔曼滤波器算法(Kalman Filter)—— 数学推导,图文并茂
  3. 校门外的树+矩阵旋转
  4. quick cocos2d-x 精灵大小(宽高)获取
  5. android navigation bar高度,Android获取屏幕真实高度包含NavigationBar(底部虚拟按键)
  6. 用Way.EntityDB进行Entity Framework Core数据库建模
  7. 如何将mysql文件导入MySQL_如何将mysql5的sql文件导入到mysql4?
  8. 许多参数和丢失的信息
  9. python 绘图的背景颜色不要_matplotlib自定义添加 “哆啦A梦”背景图,这个操作真牛逼!...
  10. android 应用无法安装程序,朋友android设备无法安装我的Android应用程序,我甚至不能为我自己做...
  11. 【Day10】项目中如何处理安全问题
  12. NOIP Day -151
  13. 联通无线网卡DNS服务器地址,全国各地电信、联通、网通的DNS服务器地址
  14. 每天一种设计模式之抽象工厂模式(Java实现)
  15. Visual Studio快捷键大全
  16. 【云图】如何制作东莞酒店地图?
  17. 智能卡 7816协议【转】
  18. Linux粘滞位简析
  19. 手机二维码识别软件3秒破译火车票信息
  20. 诛仙更新服务器正在维护,【正式服】3月7日例行更新维护公告

热门文章

  1. (2)FPGA面试技能提升篇(Perl脚本)
  2. FPGA学无止境(目录篇)
  3. journalctl -xe mysql_journalctl 日志查看方法
  4. 12018.LTC2631电压调节芯片
  5. linux input子系统分析--主要函数
  6. 位操作符:与,或,异或 狼羊菜
  7. mysql改utf8mb4后速度慢_更改MySQL数据库的编码为utf8mb4
  8. php 时间和日期,php日期和时间的应用
  9. java getpathinfo_request.getServletPath()和request.getPathInfo()用法
  10. 《深入理解分布式事务》第二章 MySQL 事务的实现原理