- Preface 前言
- 第1章 引论
- 第2章 编程惯用法
- 第3章 基础语法
- 建议19:有节制地使用 from…import 语句
- 建议20:优先使用 absolute import 来导入模块
- 建议21:i+=1 不等于 ++i
- 建议22:使用 with 自动关闭资源
- 建议23:使用 else 子句简化循环(异常处理)
- 建议24:遵循异常处理的几点基本原则
- 建议25:避免 finally 中可能发生的陷阱
- 建议26:深入理解 None 正确判断对象是否为空
- 建议27:连接字符串应优先使用 join 而不是 +
- 建议28:格式化字符串时尽量使用 .format 方式而不是 %
- 建议29:区别对待可变对象和不可变对象
- 建议30:[]、() 和 {}:一致的容器初始化形式
- 建议31:记住函数传参既不是传值也不是传引用
- 建议32:警惕默认参数潜在的问题
- 建议33:慎用变长参数
- 建议34:深入理解 str() 和 repr() 的区别
- 建议35:分清 staticmethod 和 classmethod 的适用场景
- 第4章 库
- 建议36:掌握字符串的基本用法
- 建议37:按需选择 sort() 或者 sorted()
- 建议38:使用 copy 模块深拷贝对象
- 建议39:使用 Counter 进行计数统计
- 建议40:深入掌握 ConfigParser
- 建议41:使用 argparse 处理命令行参数
- 建议42:使用 pandas 处理大型 CSV 文件
- 建议43:一般情况使用 ElementTree 解析 XML
- 建议44:理解模块 pickle 优劣
- 建议45:序列化的另一个不错的选择 JSON
- 建议46:使用 traceback 获取栈信息
- 建议47:使用 logging 记录日志信息
- 建议48:使用 threading 模块编写多线程程序
- 建议49:使用 Queue 使多线程编程更安全
- 第5章 设计模式
- 第6章 内部机制
- 建议54:理解 built-in objects
- 建议55:init() 不是构造方法
- 建议56:理解名字查找机制
- 建议57:为什么需要 self 参数
- 建议58:理解 MRO 与多继承
- 建议59:理解描述符机制
- 建议60:区别 getattr() 和 getattribute() 方法
- 建议61:使用更为安全的 property
- 建议62:掌握 metaclass
- 建议63:熟悉 Python 对象协议
- 建议64:利用操作符重载实现中缀语法
- 建议65:熟悉 Python 的迭代器协议
- 建议66:熟悉 Python 的生成器
- 建议67:基于生成器的协程及 greenlet
- 建议68:理解 GIL 的局限性
- 建议69:对象的管理与垃圾回收
- 第7章 使用工具辅助项目开发
- 第8章 性能剖析与优化
建议54:理解 built-in objects
我们知道Python中一切皆对象:字符是对象,列表是对象,内建类型(built-in type)也是对象;用户定义的类型是对象,object是对象,type也是对象。自Python2.2之后,为了弥补内建类型和古典类(classic classes)之间的鸿沟 [1]引入了新式类(new-style classes)。在新式类中,object是所有内建类型的基类,用户所定义的类可以继承自object也可继承自内建类型。
那么内建类型﹑object﹑type以及用户所定义的类之间到底有什么关系呢?它们之间本质上有什么不同吗?我们来看一个简单的例子:
class A: ... ... ... ... ... ... ... ... ①古典类A pass class B(object): pass class C(type): pass class D(dict): ... ... ... ... ... ... ... ... ②D 继承自内建类型dict pass
现在有类A﹑B﹑C﹑D的实例分别对应为a﹑b﹑c﹑d,我们来看一组求值的结果,如表6-1所示。
表6-1 不同对象求值结果
注:上表中求值时*用表中第一列的元素代替。
从表6-1中我们可以得出如下结论:
object[1]和古典类[3]没有基类,type[2]的基类为object。
新式类([4],[5],[6])中type()的值和__class__的值是一样的,但古典类[3]中实例的type为instance,其type()的值和__class__的值不一样。
继承自内建类型的用户类的实例[6]也是object的实例,object[1]是type的实例,type实际是个元类(metaclass)。
object和内建类型以及所有基于type构建的用户类[5]都是type的实例。
在古典类中,所有用户定义的类的类型都为instance。
综上,不同类型的对象之间的关系如图6-1所示。
图6-1 不同类型对象的关系图
我们知道古典类和新式类的一个区别是:新式类继承自object类或者内建类型。那么是不是可以这么理解:如果一个类定义的时候继承自object或者内建类型,那么它就是一个新式类,否则则是古典类?我们来看一个例子:
>>> class TestNewClass: ... __metaclass__ = type ... ... ... ... ①设置__metaclass__ 属性为type ... >>> >>> type(TestNewClass) <type 'type'> >>> TestNewClass.__bases__ (<type 'object'>,) >>> >>> a= TestNewClass() >>> type(a) <class '__main__.TestNewClass'> >>> a.__class__ <class '__main__.TestNewClass'> >>>
从上述例子我们可以看出,TestNewClass在定义的时候并没有继承任何类,但测试的结果表明其父类为object,它还是属于新式类。这其中的原因在于TestNewClass中设置了__metaclass__属性(关于元类的更多介绍可以参看后面的章节)。所以我们并不能简单地从定义的形式上来判断一个类是新式类还是古典类,而应当通过元类的类型来确定类的类型:古典类的元类为types.ClassType,新式类的元类为type类。
新式类相对于古典类来说有很多优势:能够基于内建类型构建新的用户类型,支持property和描述符特性等。作为新式类的祖先,Object类中还定义了一些特殊方法,如:__new__(),__init__(),__delattr__(),__getattribute__(),_setattr__(),__hash__(),__repr__(),__str__()等。object的子类可以对这些方法进行覆盖以满足自身的特殊需求,建议62中会对其中某些内容详细阐述,感兴趣的读者可以阅读。
注意
在Python中一切皆对象,type也是对象。
[1] 这里的鸿沟指的是:在2.2版本之前,类和类型并不统一,如a是古典类ClassA的一个实例,那么a.__class__返回‘class__main__ClassA’,type(a)返回<type 'instance'>。当引入新类后,比如ClassB是个新类,b是ClassB的实例,b.__class__和type(b)都是返回‘class'__main__.ClassB’。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论