Python 金融编程第二版(GPT 重译)(三)(1)https://developer.aliyun.com/article/1559326
Python 类的基础知识
本节涉及主要概念和具体语法,以利用 Python 中的 OOP。当前的背景是构建自定义类来模拟无法轻松、高效或适当地由现有 Python 对象类型建模的对象类型。在金融工具的示例中,只需两行代码即可创建一个新的 Python 类。
In [44]: class FinancialInstrument(object): ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) pass ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [45]: fi = FinancialInstrument() ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [46]: type(fi) ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[46]: __main__.FinancialInstrument In [47]: fi ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[47]: <__main__.FinancialInstrument at 0x10a21c828> In [48]: fi.__str__() ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[48]: '<__main__.FinancialInstrument object at 0x10a21c828>' In [49]: fi.price = 100 ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) In [50]: fi.price ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[50]: 100
类定义语句。²
一些代码;这里只是pass
关键字。
一个名为fi
的类的新实例。
每个 Python 对象都带有某些特殊属性和方法(来自object
);这里调用了用于检索字符串表示的特殊方法。
所谓的数据属性 —— 与常规属性相对 —— 可以为每个对象即时定义。
一个重要的特殊方法是__init__
,它在每次实例化对象时被调用。它以对象自身(按照惯例为self
)和可能的多个其他参数作为参数。除了实例属性之外
In [51]: class FinancialInstrument(object): author = 'Yves Hilpisch' ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def __init__(self, symbol, price): ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) self.symbol = symbol ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) self.price = price ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [52]: FinancialInstrument.author ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[52]: 'Yves Hilpisch' In [53]: aapl = FinancialInstrument('AAPL', 100) ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [54]: aapl.symbol ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[54]: 'AAPL' In [55]: aapl.author ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[55]: 'Yves Hilpisch' In [56]: aapl.price = 105 ![7](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/7.png) In [57]: aapl.price ![7](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/7.png) Out[57]: 105
类属性的定义(=每个实例都继承的)。
在初始化期间调用特殊方法__init__
。
实例属性的定义(=每个实例都是个别的)。
一个名为fi
的类的新实例。
访问实例属性。
访问类属性。
更改实例属性的值。
金融工具的价格经常变动,金融工具的符号可能不会变动。为了向类定义引入封装,可以定义两个方法get_price()
和set_price()
。接下来的代码还额外继承了之前的类定义(不再继承自object
)。
In [58]: class FinancialInstrument(FinancialInstrument): ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def get_price(self): ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) return self.price ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) def set_price(self, price): ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) self.price = price ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [59]: fi = FinancialInstrument('AAPL', 100) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) In [60]: fi.get_price() ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[60]: 100 In [61]: fi.set_price(105) ![7](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/7.png) In [62]: fi.get_price() ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[62]: 105 In [63]: fi.price ![8](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/8.png) Out[63]: 105
通过从上一个版本继承的方式进行类定义。
定义get_price
方法。
定义set_price
方法……
……并根据参数值更新实例属性值。
基于新的类定义创建一个名为fi
的新实例。
调用get_price()
方法来读取实例属性值。
通过set_price()
更新实例属性值。
直接访问实例属性。
封装通常的目标是隐藏用户对类的操作中的数据。添加相应的方法,有时称为getter和setter方法,是实现此目标的一部分。然而,这并不阻止用户直接访问和操作实例属性。这就是私有实例属性发挥作用的地方。它们由两个前导下划线定义。
In [64]: class FinancialInstrument(object): def __init__(self, symbol, price): self.symbol = symbol self.__price = price ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def get_price(self): return self.__price def set_price(self, price): self.__price = price In [65]: fi = FinancialInstrument('AAPL', 100) In [66]: fi.get_price() ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[66]: 100 In [67]: fi.__price ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) ---------------------------------------- AttributeErrorTraceback (most recent call last) <ipython-input-67-74c0dc05c9ae> in <module>() ----> 1 fi.__price ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) AttributeError: 'FinancialInstrument' object has no attribute '__price' In [68]: fi._FinancialInstrument__price ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[68]: 100 In [69]: fi._FinancialInstrument__price = 105 ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [70]: fi.set_price(100) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png)
价格被定义为私有实例属性。
方法get_price()
返回其值。
尝试直接访问属性会引发错误。
通过在类名前添加单个下划线,仍然可以直接访问和操作。
将价格恢复到其原始值。
注意
尽管封装基本上可以通过私有实例属性和处理它们的方法来实现 Python 类,但无法完全强制隐藏数据不让用户访问。从这个意义上说,这更像是 Python 中的一种工程原则,而不是 Python 类的技术特性。
考虑另一个模拟金融工具投资组合头寸的类。通过两个类,聚合的概念很容易说明。PortfolioPosition
类的一个实例将FinancialInstrument
类的一个实例作为属性值。添加一个实例属性,比如position_size
,然后可以计算出例如头寸价值。
In [71]: class PortfolioPosition(object): def __init__(self, financial_instrument, position_size): self.position = financial_instrument ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) self.__position_size = position_size ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) def get_position_size(self): return self.__position_size def update_position_size(self, position_size): self.__position_size = position_size def get_position_value(self): return self.__position_size * \ self.position.get_price() ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [72]: pp = PortfolioPosition(fi, 10) In [73]: pp.get_position_size() Out[73]: 10 In [74]: pp.get_position_value() ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[74]: 1000 In [75]: pp.position.get_price() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[75]: 100 In [76]: pp.position.set_price(105) ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) In [77]: pp.get_position_value() ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[77]: 1050
基于FinancialInstrument
类的实例的实例属性。
PortfolioPosition
类的私有实例属性。
根据属性计算位置值。
附加到实例属性对象的方法可以直接访问(也可能被隐藏)。
更新金融工具的价格。
根据更新后的价格计算新位置值。
Python 数据模型
前一节的示例已经突出了所谓的 Python 数据或对象模型的一些方面(参见https://docs.python.org/3/reference/datamodel.html)。Python 数据模型允许设计与 Python 基本语言构造一致交互的类。除其他外,它支持(参见 Ramalho(2015),第 4 页)以下任务和结构:
- 迭代
- 集合处理
- 属性访问
- 运算符重载
- 函数和方法调用
- 对象创建和销毁
- 字符串表示(例如,用于打印)
- 受管理的上下文(即
with
块)。
由于 Python 数据模型非常重要,本节专门介绍了一个示例,探讨了其中的几个方面。示例可在 Ramalho(2015)的书中找到,并进行了微调。它实现了一个一维,三元素向量的类(想象一下欧几里德空间中的向量)。首先,特殊方法__init__
:
In [78]: class Vector(object): def __init__(self, x=0, y=0, z=0): ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) self.x = x ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) self.y = y ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) self.z = z ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [79]: v = Vector(1, 2, 3) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [80]: v ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[80]: <__main__.Vector at 0x10a245d68>
三个预初始化的实例属性(想象成三维空间)。
名为v
的类的新实例。
默认字符串表示。
特殊方法__str__
允许定义自定义字符串表示。
In [81]: class Vector(Vector): def __repr__(self): return 'Vector(%r, %r, %r)' % (self.x, self.y, self.z) In [82]: v = Vector(1, 2, 3) In [83]: v ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[83]: Vector(1, 2, 3) In [84]: print(v) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Vector(1, 2, 3)
新的字符串表示。
abs()
和bool()
是两个标准的 Python 函数,它们在Vector
类上的行为可以通过特殊方法__abs__
和__bool__
来定义。
In [85]: class Vector(Vector): def __abs__(self): return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5 ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def __bool__(self): return bool(abs(self)) In [86]: v = Vector(1, 2, -1) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [87]: abs(v) Out[87]: 2.449489742783178 In [88]: bool(v) Out[88]: True In [89]: v = Vector() ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [90]: v ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[90]: Vector(0, 0, 0) In [91]: abs(v) Out[91]: 0.0 In [92]: bool(v) Out[92]: False
返回给定三个属性值的欧几里德范数。
具有非零属性值的新Vector
对象。
仅具有零属性值的新Vector
对象。
如多次显示的那样,+
和*
运算符几乎可以应用于任何 Python 对象。其行为是通过特殊方法__add__
和__mul__
定义的。
In [93]: class Vector(Vector): def __add__(self, other): x = self.x + other.x y = self.y + other.y z = self.z + other.z return Vector(x, y, z) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar, self.z * scalar) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [94]: v = Vector(1, 2, 3) In [95]: v + Vector(2, 3, 4) Out[95]: Vector(3, 5, 7) In [96]: v * 2 Out[96]: Vector(2, 4, 6)
在这种情况下,两个特殊方法都返回自己的类型对象。
另一个标准的 Python 函数是 len()
,它给出对象的长度,即元素的数量。当在对象上调用时,此函数访问特殊方法 __len__
。另一方面,特殊方法 __getitem__
使通过方括号表示法进行索引成为可能。
In [97]: class Vector(Vector): def __len__(self): return 3 ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) def __getitem__(self, i): if i in [0, -3]: return self.x elif i in [1, -2]: return self.y elif i in [2, -1]: return self.z else: raise IndexError('Index out of range.') In [98]: v = Vector(1, 2, 3) In [99]: len(v) Out[99]: 3 In [100]: v[0] Out[100]: 1 In [101]: v[-2] Out[101]: 2 In [102]: v[3] ---------------------------------------- IndexErrorTraceback (most recent call last) <ipython-input-102-0f5531c4b93d> in <module>() ----> 1 v[3] <ipython-input-97-eef2cdc22510> in __getitem__(self, i) 7 elif i in [1, -2]: return self.y 8 elif i in [2, -1]: return self.z ----> 9 else: raise IndexError('Index out of range.') IndexError: Index out of range.
Vector
类的所有实例都有长度为三。
最后,特殊方法 __iter__
定义了对对象元素进行迭代的行为。定义了此操作的对象称为可迭代的。例如,所有集合和容器都是可迭代的。
In [103]: class Vector(Vector): def __iter__(self): for i in range(len(self)): yield self[i] In [104]: v = Vector(1, 2, 3) In [105]: for i in range(3): ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) print(v[i]) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) 1 2 3 In [106]: for coordinate in v: ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) print(coordinate) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) 1 2 3
使用索引值进行间接迭代(通过__getitem__
)。
对类实例进行直接迭代(使用__iter__
)。
提示
Python 数据模型允许定义与标准 Python 操作符、函数等无缝交互的 Python 类。这使得 Python 成为一种相当灵活的编程语言,可以轻松地通过新类和对象类型进行增强。
总之,子节 “向量类” 在单个代码块中提供了 Vector
类的定义。
结论
本章从理论上和基于 Python 示例介绍了面向对象编程(OOP)的概念和方法。OOP 是 Python 中使用的主要编程范式之一。它不仅允许建模和实现相当复杂的应用程序,还允许创建自定义对象,这些对象由于灵活的 Python 数据模型,与标准 Python 对象表现得相似。尽管有许多批评者反对 OOP,但可以肯定地说,当达到一定复杂程度时,它为 Python 程序员和量化人员提供了强大的手段和工具。在 [Link to Come] 中讨论和展示的衍生定价包呈现了这样一个情况,其中 OOP 似乎是唯一合理的编程范式,以处理固有的复杂性和对抽象的需求。
Python 金融编程第二版(GPT 重译)(三)(3)https://developer.aliyun.com/article/1559335