在编写代码时,我们通常不会毫无规划地逐条执行语句和指令,而是先有模块化(Modularization)抽象设计,希冀能使代码最大程度地重复使用,同时将设计、调试和维护等的复杂度降至最低。
函数也是模块化设计的一种。而面向对象编程(Object-Oriented Programming)更是模块化设计的重要方法。
面向对象的概念
面向对象这种编程范式(Programming Paradigm)认为世界是由各式各样的对象所组成的,不同的对象之间可以相互作用和通讯。
类和对象的含义
说到面向对象就不得不说类(Class),和对象的含义,比如说把一个汽车看成一个对象,那么这个小汽车对象包含的信息可能有轮胎型号、发动机型号、方向盘型号等等。这些描述对象特征的数据信息称为这个对象的属性(Attribute);而存取属性的函数称为方法(Method),是对象与外界沟通的接口。
具有相同属性与方法的对象,构成了一个类(Class)。类其实就是一种将对象抽象化而形成的概念,简单来说对象就是类的实例化(Instance)。
面向对象编程的三大特征
“面向对象编程”的思想起源于20世纪60年代的Simula语言,并逐渐在各种语言上发展出三大特征:“封装”、“继承”、“多态”。
类
划重点:
在Python这种动态语言中,属性与方法可以动态绑定,故同一类所产生的实例,未必具有同样的属性或方法!!!!
一个类的不同实例
1 | class Asset(object): |
看上面的例子,首先讲讲小括号里的object,小括号里面填写的是现在定义的类继承自哪一类。如果没有明显继承自哪一类,则填object,object是所有类继承的源头。
然后我们来看第四行和第六行,asset1和asset2都是创建自Asset类的一个实例。而第五行和第七行则是给实例各自绑定了一个代码属性。
一个类拥有共同特征
上面的例子演示了源于同一个类的不同实例,藉由动态绑定可以拥有不同的属性。这样做虽然自由,但是有悖于把类视为实例的模版惯例,也不利于软件开发与维护。如果我们希望创建同一个类的实例拥有一些共同的特征,那么我们可以定义一个特殊的方法:1
2
3
4
5
6
7
8
9class Asset(object):
pass
def __init__(self, id, price):
self.id = id
self.price = price
asset3 = Asset('003',100.5)
print(asset3.id,asset3.price)
## 输出003 100.5
首先这样写的目的在于绑定一些在创建实例时非填不可的属性。
那么我们就来看看init函数的参数,首先self指的是被实例化出来的对象,一般不用传递进来,id和price则是用于给对象的属性赋值,这两个必须进行参数传递,如果在init方法中绑定的属性,在创建实例时没有传入相应的参数,那么Python就会报错。
个人理解
在我个人的理解init这个函数,就相当于C++的构造函数,JAVA的构造方法,作用就是定义了Python类中的变量,然后对变量进行初始化。
题外话
当然我们在编写Python类时,可以创建一个AssetClass.py的文件,专门存放Asset()类的定义代码。此外,创建完类的名称以后,加入一些文字说明,简要阐述类的主要内容,以方便使用者快速了解此类功能,这些文字储存成字符串形式。
封装
如果我们要打印上例中的代码id,则可以定义一个打印id的函数,我们通过调用函数,就可以将其打印出来:1
2
3
4
5
6
7
8
9
10
11def print_id(asset):
print('The id of the asset is: %s' %(asset.id))
class Asset(object):
pass
def __init__(self, id, price):
self.id = id
self.price = price
asset3 = Asset('003',100.5)
print_id(asset3)
## 输出The id of the asset is: 003
但是id属于asset3这个对象的内部属性。从软件设计的角度考虑,有些属性可能携带重要的信息或具有某些隐私性,像上例那样能够轻易地从外部函数直接访问内部属性,并不利于数据存取权限的管理。比较好的数据管理的做法是,把属性和访问属性的方法放在同一个对象中,使得信息载体和存取信息“藉由对象关联起来”。于是,我们作出了如下的修改:1
2
3
4
5
6
7
8
9
10
11
12class Asset(object):
pass
def __init__(self, id, price):
self.id = id
self.price = price
def print_id(self):
print('The id of the asset is: %s' %(self.id))
asset5 = Asset('005',99.9)
asset5.print_id()
## 输出The id of the asset is: 005
在类中定义的方法,我们可以用“实例.方法”进行调用。
这样做的另一个好处是,可以把方法的实现逻辑隐藏在类或对象的内部。但是这样仍然不能保证对象的属性被无关的函数所改变。为了限制它们不被外界访问。在Python中,属性变量前加上两个下划线表示private属性,private属性的存取只能在类的内部进行。所以我们又作出了改变:1
2
3
4
5
6
7
8
9
10
11
12class Asset(object):
pass
def __init__(self, id, price):
self.__id = id
self.__price = price
def get_ID(self):
return self.__id
asset6 = Asset('006',5.21)
print("The id of the asset is: %s"%(asset6.get_ID()))
## 输出The id of the asset is: 006
由于private属性只能在类的内部可见,试图通过“实例.属性名称”从外部访问,那么Python就会报错。
所以我们就在类的内部写了一个函数来获得id就解决了这个问题。
继承
父类(Parent Class)与子类(Child Class)
继承的思想在于充分利用已有的类的功能,在已有类的基础上扩展来定义新的类。而原有的类称为父类,由父类衍生出的类称为子类。一个父类可以衍生出多个子类。子类一旦继承了父类,就拥有了父类的属性和方法,并且可以按照特殊要求对其增添与修改。这么做的好处之一就在于代码的复用性。
首先我们定义一个父类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from datetime import datetime
class Asset(object):
share = 0
buyTime = datetime.strptime('1900-01-01','%Y-%m-%d')
sellTime = datetime.strptime('1900-01-01', '%Y-%m-%d')
def __init__(self, id, price):
self.id = id
self.price = price
def buy(self, share, time):
self.share += share
self.buyTime = datetime.strptime(time,'%Y-%m-%d')
def sell(self, share, time):
self.share -= share
self.sellTime = datetime.strptime(time, '%Y-%m-%d')
OK,但是我们假设有些金融商品是不能卖空的,那么我们可以定义一个NonShortableAsset类来刻画这些不能卖空的金融商品,我们把其作为子类,直接继承父类Asset。再对Sell方法加上不能卖空的限制:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27from datetime import datetime
class Asset(object):
share = 0
buyTime = datetime.strptime('1900-01-01','%Y-%m-%d')
sellTime = datetime.strptime('1900-01-01', '%Y-%m-%d')
def __init__(self, id, price):
self.id = id
self.price = price
def buy(self, share, time):
self.share += share
self.buyTime = datetime.strptime(time,'%Y-%m-%d')
def sell(self, share, time):
self.share -= share
self.sellTime = datetime.strptime(time, '%Y-%m-%d')
class NonShortableAsset(Asset):
def sell(self, share, time):
if self.share >= share:
self.share -= share
self.sellTime = datetime.strptime(time, '%Y-%m-%d')
else:
print('Not permitted!There are not enough share to sell!')
print('Current share is ',self.share)
return
通过继承,我们在定义子类时,不必大费周章地重复父类已经拥有的内容,只需要增加或修改想要的东西即可。
当然我们也注意到当子类与父类有相同的方法时,子类的方法会覆盖(重写)父类的方法!
本文部分整理转载自《量化投资:以Python为工具》蔡立耑 仅供学习研究之用,侵删。