把数据和行为放在一起
到目前为止,持仓可以是元组或字典,由另外的函数去处理。类(class)把数据和属于它们的函数打包成一种新类型。class Position: 定义蓝图;调用它,Position("AAPL", 100, 180.0),就会按这份蓝图构建一个对象(也叫实例)。每个对象保存自己的数据,类中定义的函数称为方法,作用于该对象。
class Position:
def __init__(self, symbol, quantity=0):
self.symbol = symbol # attributes belong to this object
self.quantity = quantity
def describe(self):
return f"{self.quantity} x {self.symbol}"
apple = Position("AAPL", 100)
tesla = Position("TSLA")
print(apple.describe()) # 100 x AAPL
print(tesla.describe()) # 0 x TSLA
print(apple.quantity, tesla.quantity) # 100 0__init__ 与 self
__init__ 是构造方法:创建对象时 Python 会自动运行它,由它保存初始值。每个方法都会把对象本身作为第一个参数接收,按惯例叫 self;你不用传,Python 会代劳(apple.describe() 等同于 Position.describe(apple))。通过 self.name = value 保存的内容是该对象的属性,在多次方法调用之间一直保留。__init__ 只负责初始化,不返回任何东西。
class Account:
def __init__(self, cash):
self.cash = cash
def deposit(self, amount):
self.cash += amount # changes this object's attribute
return self.cash
first = Account(1000)
second = Account(50)
first.deposit(500)
print(first.cash, second.cash) # 1500 50 (separate objects, separate data)
same = first # a second name for the same object
same.deposit(1)
print(first.cash) # 1501改变状态或回答问题的方法
有些方法回答关于对象的问题而不改变它(market_value(price));它们只应返回结果,不动属性。另一些方法改变对象(apply_fill),更新属性,并常常返回有用的值,比如已实现盈亏。一个方法可以用 self.other_method(...) 调用另一个,让规则集中在一处:如果 unrealized_pnl 需要市值,就调用 self.market_value(price),而不是重复公式。
class Position:
def __init__(self, symbol, quantity=0, avg_price=0.0):
self.symbol = symbol
self.quantity = quantity
self.avg_price = avg_price
def market_value(self, price):
return self.quantity * price
def unrealized_pnl(self, price):
return self.market_value(price) - self.quantity * self.avg_price
pos = Position("AAPL", 100, 180.0)
print(pos.market_value(185.0)) # 18500.0
print(pos.unrealized_pnl(185.0)) # 500.0陷阱与何时使用类
忘记写 self. 是典型的疏忽:方法里的 quantity = 5 只会创建一个用完即弃的局部变量,而不是属性。类名用 CapWords(Position),属性和方法用小写加下划线;以下划线开头(_cache)表示内部使用。永远不要用 def __init__(self, fills=[]) 这样的可变默认值:所有对象会共享那一个列表;应使用 fills=None,在内部再创建 []。当数据和有关它的规则总是一起出现时再建类;普通函数或字典往往更简单。
class Wrong:
def __init__(self, quantity):
quantity = quantity # forgot self.: nothing is stored
class Shared:
def __init__(self, fills=[]): # one list shared by every object
self.fills = fills
class Right:
def __init__(self, fills=None):
self.fills = [] if fills is None else list(fills)
a, b = Shared(), Shared()
a.fills.append(100)
print(b.fills) # [100] (surprise!)
c, d = Right(), Right()
c.fills.append(100)
print(d.fills) # []
print(hasattr(Wrong(5), "quantity")) # Falseclass Name:定义类型;Name(args)构建对象并运行__init__。self.attr = value把数据存到对象上;每个方法首先接收self。- 查询方法返回答案;更新方法修改属性;避免可变默认值。
下一节:特殊方法,如 __repr__ 和 __eq__,让你的对象能漂亮地打印并按预期比较。
实验:评分内容
- 用
__init__构建一个持仓25 分 - 用
market_value计算持仓市值25 分 - 用
unrealized_pnl计算浮动盈亏25 分 - 用
apply_fill在成交后更新持仓25 分