Python中新式类与经典类的区别详析

1.新式类与经典类

在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。

“新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。

官方文档 https://www.python.org/doc/newstyle/

2.继承顺序的区别

主要是在多重继承时才会遇到这个问题。

经典类的钻石继承是深度优先,即从下往上搜索;新式类的继承顺序是采用C3算法(非广度优先)。

对经典类进行代码验证(所有经典类的代码必须在Python2下运行,下同),ClassicClassB 继承自 ClassicClassA,SubClassicClass继承自ClassicClassB,ClassicClassC:

class ClassicClassA():
 var = 'Classic Class A'

class ClassicClassB(ClassicClassA):
 pass

class ClassicClassC():
 var = 'Classic Class C'

class SubClassicClass(ClassicClassB, ClassicClassC):
 pass

if __name__ == '__main__':
 print(SubClassicClass.var)

在SubClassicClass对var属性进行搜索的过程中,根据从下到上的原则,会优先搜索ClassicClassB,而ClassicClassB没有var属性,会继续往上搜索ClassicClassB的超类ClassicClassA,在ClassicClassA中发现var属性后停止搜索,var的值为ClassicClassA中var的值;而ClassicClassC的var属性从始至终都未被搜索到。

从运行结果可以看出,输出的是Classic Class A,可见类继承的搜索是深度优先,由下至上进行搜索。

Classic Class A

新式类的继承顺序并非是广度优先,而是C3算法,只是在部分情况下,C3算法的结果恰巧与广度优先的结果相同。

对新式类的继承搜索顺序进行代码验证,新式类中,可以使用mro函数来查看类的搜索顺序(这也算是一个区别),如SubNewStyleClass.mro()。

class NewStyleClassA(object):
 var = 'New Style Class A'

class NewStyleClassB(NewStyleClassA):
 pass

class NewStyleClassC(NewStyleClassA):
 var = 'New Style Class C'

class SubNewStyleClass(NewStyleClassB, NewStyleClassC):
 pass

if __name__ == '__main__':
 print(SubNewStyleClass.mro())
 print(SubNewStyleClass.var)

从代码运行结果看,恰巧与从左至右的广度优先预期结果相同。

[<class '__main__.SubNewStyleClass'>, <class '__main__.NewStyleClassB'>, <class '__main__.NewStyleClassC'>, <class '__main__.NewStyleClassA'>, <type 'object'>]
New Style Class C

但是不代表新式类的继承顺序就是广度优先,可以稍微修改下代码进行验证:NewStyleClassC改为继承自object

class NewStyleClassA(object):
 var = 'New Style Class A'

class NewStyleClassB(NewStyleClassA):
 pass

class NewStyleClassC(object):
 var = 'New Style Class C'

class SubNewStyleClass(NewStyleClassB, NewStyleClassC):
 pass

if __name__ == '__main__':
 print(SubNewStyleClass.mro())
 print(SubNewStyleClass.var)

运行结果不再符合广度优先:

[<class '__main__.SubNewStyleClass'>, <class '__main__.NewStyleClassB'>, <class '__main__.NewStyleClassA'>, <class '__main__.NewStyleClassC'>, <type 'object'>]
New Style Class A

可见,新式类的继承顺序并非广度优先,而是C3算法。至于C3算法,以后再另外详细写。

3.类实例类型的区别

在经典类中,所有的类都是classobj类型,而类的实例都是instance类型。类与实例只有通过__class__属性进行关联。这样在判断实例类型时,就会造成不便:所有的实例都是instance类型。

class A():pass
class B():pass

a = A()
b = B()

if __name__ == '__main__':
 print(type(a))
 print(type(b))
 print(type(a) == type(b))

type(a) == type(b)的结果永远为True,那这样的比较就毫无意义。

更为麻烦的是,经典类的实例是instance类型,而内置类的实例却不是,无法统一。

通过代码判断下内置类型list的实例[1, 2, 3]是什么类型

print(type([1, 2, 3]))

运行结果,是list类型

<type 'list'>

内置类的实例类型和经典类的实例类型完全不同,容易造成困惑,不利于代码的统一。

这个问题在Python 3之后就不复存在了,因为Python3中所有的类都是新式类,新式类中类与类型已经统一:类实例的类型是这个实例所创建自的类(通常是和类实例的__class__相同),而不再是Python 2.x版本中的“instance”实例类型。

更详细的:https://www.jb51.net/article/165048.htm

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • 浅谈python之新式类

    前言 本文中代码运行的python版本一律采取2.7.13 科普: 经典类:classic class 新式类:new-style class python2.2 之前并没有新式类 python2.2-2.7 新式类与经典类并存, 默认使用经典类, 除非显式继承object python3.X 中去除了经典类, 用户定义的所有类都隐式继承自object  如何使用新式类 class New(object): # 显式继承object类 pass class Old: pass class Old

  • 浅谈python新式类和旧式类区别

    python的新式类是2.2版本引进来的,我们可以将之前的类叫做经典类或者旧式类. 为什么要在2.2中引进new style class呢?官方给的解释是: 为了统一类(class)和类型(type). 在2.2之前,比如2.1版本中,类和类型是不同的,如a是ClassA的一个实例,那么a.__class__返回 ' class    __main__.ClassA' ,type(a)返回总是<type 'instance'>.而引入新类后,比如ClassB是个新类,b是ClassB的实例,b

  • Python中对象的比较操作==和is区别详析

    前言 Python 中对象的比较有两种方式 == 和 is.两种方式都能判断操作符两侧的变量值是否相等,那么它们的区别是什么呢?通过下面的介绍我们来一探究竟. 比较操作符通常用于条件语句,如下示例: if a == b: pass if a is False: pass == 与 is 的区别 == 操作符比较对象的值是否相等.小明有一块 劳力士 手表,小李也有一块同款 劳力士 手表,这时我们就认为这两块手表相等. 小明的手表 = 劳力士 小李的手表 = 劳力士 小明的手表 == 小李的手表 i

  • python中的数组赋值与拷贝的区别详解

    具体的注解我已经写在了程序里面:通俗的解释了python里面的浅拷贝与深拷贝的不同,请看程序. # -*- coding: utf-8 -*- import numpy as np import copy as cp import matplotlib.pyplot as plt import time import math fig = plt.figure() ax = fig.add_subplot(241) # 定义一个多维数组 x = np.array([[1, 2, 3], [4,

  • python中random.randint和random.randrange的区别详解

    在python中,通过导入random库,就能使用randint 和 randrange 这两个方法来产生随机整数.那这两个方法的区别在于什么地方呢?让我们一起来看看! 区别: randint 产生的随机数区间是包含左右极限的,也就是说左右都是闭区间的[1, n],能取到1和n.而 randrange 产生的随机数区间只包含左极限,也就是左闭右开的[1, n),1能取到,而n取不到.randint 产生的随机数是在指定的某个区间内的一个值,而 randrange 产生的随机数可以设定一个步长,也

  • Python中函数eval和ast.literal_eval的区别详解

    前言 众所周知在Python中,如果要将字符串型的list,tuple,dict转变成原有的类型呢? 这个时候你自然会想到eval. eval函数在python中做数据类型的转换还是很有用的.它的作用就是把数据还原成它本身或者是能够转化成的数据类型.下面来看看示例代码: string <==> list string <==> tuple string <==> dict 也就是说,使用eval可以实现从元祖,列表,字典型的字符串到元祖,列表,字典的转换,此外,eval

  • python中response.text 和response.content的区别详解

    1.response.text - 类型:str - 解码类型: 根据HTTP 头部对响应的编码作出有根据的推测,推测的文本编码 - 如何修改编码方式:response.encoding="gbk" 2. response.content - 类型:bytes - 解码类型: 没有指定 - 如何修改编码方式:response.content.deocde("utf-8") 3.获取网页源码的通用方式: response.content.decode() respon

  • Mysql中tinyint(1)和tinyint(4)的区别详析

    目录 1.varchar(M)和数值类型tinyint(M) 的区别 2测试 总结 1. varchar(M)和数值类型tinyint(M) 的区别 字符串类型:varchar(M)而言,M是字段中可以存储的最大字符串,也就是说字段长度.根据设置,当你插入的数值超过字段设置的长度时,很有可能会收到错误提示,如果没有收到提示,插入的数据也有可能被自动的截断以适应该字段的预定义长度.所有像varchar(5)表示其存储的字符串长度不能超过5. 数值列类型:其长度修饰符表示最大宽度,与该字段物理存储没

  • mysql中drop、truncate与delete的区别详析

    目录 1.drop:删除数据库 2.对比TRUNCATETABLE和DELETEFROM 3.DDL和DML的说明 4.效率对比 总结 1. drop:删除数据库 drop语句将表所占用的空间全释放掉. drop > truncate > delete 方式1:如果要删除的数据库存在,则删除成功.如果不存在,则报错 DROP DATABASE mytest1; 方式2:推荐. 如果要删除的数据库存在,则删除成功.如果不存在,则默默结束,不会报错. DROP DATABASE IF EXISTS

  • Python中新式类与经典类的区别详析

    1.新式类与经典类 在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于"新式类",都会获得所有"新式类"的特性:反之,即不由任意内置类型派生出的类,则称之为"经典类". "新式类"和"经典类"的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的

  • python新式类和经典类的区别实例分析

    本文实例讲述了python新式类和经典类的区别.分享给大家供大家参考,具体如下: 新式类就是  class person(object): 这种形式的, 从py2.2 开始出现的 新式类添加了: __name__ is the attribute's name. __doc__ is the attribute's docstring. __get__(object) is a method that retrieves the attribute value from object. __se

  • 对python中不同模块(函数、类、变量)的调用详解

    首先,先介绍两种引入模块的方法. 法一:将整个文件引入 import 文件名 文件名.函数名( ) / 文件名.类名 通过这个方法可以运行另外一个文件里的函数 法二:只引入某个文件中一个类/函数/变量 需要从某个文件中引入多个函数或变量时,用逗号隔开即可 from 文件名 import 函数名,类名,变量名 接下来,通过一个具体的例子说明引入 模块的具体方法: 假设新建一个python包test2,里边有一个名为run.py的python文件,run.py文件里有一个名为running()的函数

随机推荐