转自 https://zhuanlan.zhihu.com/p/50916846
简介
jqdatasdk 是聚宽的一个模块,主要用于从本地获取聚宽的金融数据,方便在本地进行量化研究,或者对接本地使用的交易系统。
如果要便捷的使用 jqdatasdk 的话,你可能会希望定义一个自定义的类,然后对 jqdatasdk 的 API 进行二次封装,从而实现较高自由度的调用,或者实现一些比较复杂的功能。
这里介绍一下如何借鉴 python 装饰器的技术来实现批量二次封装 jqdatasdk 的 API。其实这个方法同样也适用于二次封装其他的 API。
阅读之前
在阅读本文之前,你可能需要具备如下知识:
- python 基础
- python 面向对象的编程
- python 函数式编程:主要是装饰器、可变参数、关键字参数
- python 魔法方法
注意:为了简化代码,下面演示的代码中有部分是伪代码。
为了区别自然语言” 方法 “和编程中面向对象的” 方法 “, 以免理解起来有混淆,我用中英文进行区分,编程的方法用英文 method 来表示。
开始
在批量操作之前,先介绍一下最简单的二次封装 API 的方法,方便进行理解。
最简单的方法,就是直接创建一个同名的 method,然后指向 jqdatasdk 的 method。
优点是灵活度高,比如可以增加调用前的判断条件,调用结束后运行其他 method 等。缺点是麻烦,机械劳动太多,而且代码重复量大。如果要二次封装的 API 数量很多的话,显然不适合。
import jqdatasdk
class JQData():
def __init__(self):
self._sdk = jqdatasdk
def get_price(self, *args, **kwargs):
if condition:
return self._sdk.get_price(*args, **kwargs)
else:
do_somthing()
如果不想做这么多重复工作的话,可以用__getattr__这个魔法 method 来实现代理调用。
简单来说,getattr__就是当调用一个没有定义的属性或者 method 时进行的操作。因此我们可以写一个__getattr,当调用 jqdatasdk 的 method 时,因为我们的类中没有这些 method,所以直接借助 getattr (),返回了 jqdatasdk 的 method,从而实现代理访问(注意:__getattr__和 getattr 是有区别的,具体区别可以自己去搜索)。
但是,这里也有一个明显的缺点,就是它是直接调用 API,不能像上面的方法一样,加入一些其他的操作。
import jqdatasdk
class JQData():
def __init__(self):
self._sdk = jqdatasdk
def __getattr__(self, name):
# 这里还可以自己定义哪些方法要进行代理访问。
methods = [i for i in self._skd.__dict__ if i.startswith('get_')]
if name in methods:
return getattr(self._sdk, name)
因此,我们需要一个方法,既可以省去机械劳动,减少代码重复量,又可以实现比较高的灵活度,方便加入一些其他操作。这里,我们借鉴一下 python 装饰器的思路。
在这里,我们让__getattr__不要直接返回 jqdatasdk 的 method,而是运行了一个 "中间"method,而运行这个 "中间"method 返回的一个是真正的调用 API 的函数(注意:返回的对象是一个函数)。这个函数可以定义一些自己的操作。
如果觉得不是很好的理解的话,可能需要先去看看装饰器方面的内容。
class JQData():
def __init__(self):
self._sdk = jqdatasdk
def __getattr__(self, name):
# 这里还可以自己定义哪些方法要进行代理访问。
methods = [i for i in self._skd.__dict__ if i.startswith('get_')]
if name in methods:
return self._run_func(name)
def _run_func(self, name):
def wrapper(*args, **kwargs):
if condition:
func = getattr(self._sdk, name)
df = func(*args, **kwargs)
self.do_something(df)
return df
else:
do_otherthing()
return wrapper