2019-11-26   更新

03 Python 关键点讲解:面向对象的机制
袁霄

教你用 Python 进阶量化交易

袁霄 · 全栈工程师

读一本好书,就是和许多高尚的人谈话。

——歌德

本节开始讲解量化交易系统最基本的编程工具 Python 的关键知识点。我们经过调研发现,目前市面上量化交易相关的书籍、课程等教学产品多数从最基础的安装、语法、函数…开始介绍 Python 这个工具,我们认为 Python 基础知识的介绍太过千篇一律,同学们上网就能搜索到基础的知识,并没有必要在此处付费去学习。另一方面 Python 这门语言工具虽然入门简单,但是深层次的一些概念并不容易理解,这里将针对于 Python 在量化交易应用中的一些关键点进行重点剖析,使同学们对 Python 语言工具有更深层次的理解。

首先我们从全局概念上先来了解 Python 面向对象的机制。面向对象是一种建立在“对象”(object)概念上来指导软件开发的方法,认为客观世界由各种对象组成,任何事物都是对象。Python 是一门面向对象的语言,因此在 Python 中一切皆为对象。接下来我们将围绕以下这幅拓扑图来描述Python 面向对象机制。

图片描述

拓扑图中,从左至右,框图分别代表了元类、类/类型、实例。在面向对象体系中主要存在“类”和“实例”、“父类”和“子类”这两种关系,图中虚线描述了“类”和“实例”关系, 实线描述“父类”和“子类”关系。那么我们开始逐一来介绍图中的各个元素及彼此间的关系。

1. “类”和“实例”及“父类”和“子类”的关系

在介绍“类”和“实例”及“父类”和“子类”的关系前,我们先介绍下“类”和“抽象类”。我们知道,“类”是从一堆对象中以抽象的方式把相同的特征归类得到的,由于“类”本身也是对象,那么更进一步抽象可以得到“抽象类”,两者的区别在于前者是从现实对象抽象而来的,后者是基于类抽象而来的。这里举个形象的例子来说明下,如下图所示:

图片描述
比如科比和梅西这两个对象抽象得到的共同特征是人,那么人作为科比和梅西的一个“类”,同理将得到的人、猫、蛇这三个“类”更进一步抽象得到的共同特征是动物,那么动物作为人、猫、蛇这三个“类”的“抽象类”。

现在我们再回过来介绍下“类”和“实例”及“父类”和“子类”的关系。“父类”和“子类”关系指的是子类继承父类的特征,同时“子类”还可以拥有自己的特征,比如人、猫、蛇这三个“类”继承了动物这个“抽象类”的特征,同时又拥有人、猫、蛇各自的特征,此时“动物”称为“父类”或“超类(Super class)、基类(Base class)”,人、猫、蛇称为“子类”或“派生类”。“类”和“实例”关系指的是“实例”对象是“类”对象的具体实现,比如有个人叫“科比”,那么“科比”就是“人”的一个实例。

总的来看,无论是继承还是实例化,它们的前提是抽象过程,只有抽象得到“抽象类”和“类”后才能展开继承和实例化过程, 这个机制的好处是可以在不改变“类”代码的基础上改变原有的功能,实现代码的重用。需要注意的是,“抽象类”只能被继承,不能被实例化,好比我们有香蕉的类、苹果的类、桃子的类,从这些类抽取相同的特征就是水果这个“抽象类”,我们吃水果时要么是吃一个具体的香蕉,要么是吃一个具体的桃子,是无法吃到一个叫做水果的东西的。

2. “元类”和“类”以及'object''type'的关系

我们以拓扑图中的内置类型 list 为例,结合拓扑图和例程代码我们发现 list 的“父类”是(<type 'object'>),对于 list 的一个实例 mylist,它的类型是 <type 'list'>,由于“抽象类”只能被继承,不能被实例化,所以 mylist 并不存在“父类”。同理,对于内置类型 tupledictint 也是一样。我们看到类/类型框图中 list 与元类框图中 type 之间是以虚线箭头连接的,也就是说 list 的类型是<type 'type'>,这又是怎么回事呢?

# 例程代码
# __class__ 查看 list 的类型
print list.__class__ # 结果:<type 'type'> 
# __bases__ 查看 list 的父类
print list.__bases__ # 结果:(<type 'object'>,) 

mylist = [1,2,3]
# __class__ 查看 mylist 的类型
print mylist.__class__ # 结果: <type 'list'>
# __bases__ 查看 mylist 的父类
print mylist.__bases__ # 结果:AttributeError: 'list' object has no attribute '__bases__'

既然说到这个地方了,我们需要介绍下元类(metaclass)。尽管 Tim Peters 说 99% 的程序猿都不需要用到元类,但是这里我们还是要对它有一个大致的了解,知道它的机制和作用,从而对 Python 面向对象有一个更全面的理解。

对象都是“类”实例化的结果,其实“类”也是对象,那它是谁实例化的结果呢?顾名思义 metaclass 就是创建“类”的模板,也就是创建“类”对象的“类”,我们称它为元类,当我们需要创造一个全新的类时可以使用元类来实现。总的关系可以归纳为,metaclass 实例化产生 class,class 实例化的结果是 instance。如下图所示:

图片描述
另一个问题,元类也是对象,那它是由谁实例化得到的?此处重点观察下类/类型框图中 <type 'object'> 与元类框图中 <type 'type'> 之间的关系。在拓扑图中我们看到,<type 'type'><type 'object'> 之间同时有虚线和实线连接,object 是 type 的实例(用虚线连接),type 本身也是自己的实例(用虚线连接),同时 object 自己已经是所有类的父类(用实线连接),包括也是 type 的父类(用实线连接),因此 object 并没有“父类”。总而言之,object 和 type 是 Python 中的两个源对象,它们互相依赖对方来定义。所以在 Python 中这个追溯终止在 type 类,即元类是 type 类或其子类,而 type 类的元类就是它自己,否则会像鸡生蛋、蛋生鸡的问题那样一直下去就没有尽头了。

#例程代码
# object 是 type 的实例 .type() 方法可获取对象的类型。
print type(object) # 结果:<type 'type'>  
print object.__class__  # 结果: <type 'type'> 

# object 并没有父类
print object.__bases__  # 结果:() 

# type 本身也是自己的实例
print type.__class__ # 结果:<type 'type'> 

# object是type的父类
print type.__bases__ # 结果:(<type 'object'>,) 

这里有两个面向对象的推论规则需要注意下。由于 object 是 type 的实例,同时 list、tuple、dict 是 object 的子类,那么 list、tuple、dict 也是type 的实例。另外 mylist 是 list 的实例,同时 list 又是 object 的子类,那么 mylist 也是 object 的实例。

3. 经典类(classic classes)和新式类(new-style classes)的区别

在拓扑图中除了 Python 内置类型外,我们可以通过 class 语句继承一个已存在的类/类型而创建一个新的类/类型,比如 class C(object) 继承了“父类” ‘object’ 的特征。这里有必要说下 Python 经典类 classic classes和新式类 new-style classes 的区别,classic classes 的元类是types.ClassType,而 new-style classes 的元类是 type,也就是说所有的新式类都是由 type 类实例化创建而来。在 Python 2.x 及以前的版本中我们由 class 语句创建的类对象是经典类,内置类型属于新式类,所以我们并不将类和类型混为一体。目前官方文档已经公布了消息,如下图所示:

图片描述

在 Python 2.7 版本中已经集成了新式类,而经典类在 Python 3.x 中被移除,也就是说在 Python3.x 及之后的版本,类和类型已经合并为一体。

我们可以通过例程代码来验证下,比如在 Python 2.7 版本中运行以下代码可知 C 的类型为老式的 classobj,如果 C 显式继承了超类 ‘object’ 就成为了新式类,Python 3.x 的运行结果与 class C 显式继承 ‘object’ 完全相同,进一步印证在 Python 3.x 中所有的类都是新式类,即显示或隐式继承自object 类。

# 例程代码
# class C 类型为老式的 classobj
class C():pass
print(type(C)) # 结果: <type 'classobj'>

# class C 显式继承 'object',成为新式类
class C(object):pass
print(type(C)) # 结果: <type 'type'>

4. 小结

Python 作为一门高级脚本语言的确上手很快,丰富的标准库可以帮助我们完成各种工作,但是当我们需要着手搭建一个系统的时候,我们需要考虑软件的可移植性、可扩展性、可维护性等等,因此我们必须深入去研究 Python 的机制,才能更好地利用它。本节对 Python 的面向对象机制进行了深入剖析,对“元类”、“类”、“实例”、“父类”、“类型”等非常重要却又容易混淆的概念和相互的关系进行详细讲解,希望能够帮助同学们理解 Python 的面向对象机制,设计出更有层次架构的软件代码。

0 / 2
登录后可任选 2 个小节免费阅读
课程目录
取消 评论 发送