Python编码的最佳实践总结

论坛 期权论坛 期权     
真格量化   2019-7-22 09:34   6455   0
工作中很多开发者都在用Python,但往往很少有人关注它的性能和惯用法,一般都是现学现用,毕竟Python与C语言等老牌语言相比诞生时间不长,不是开发者在学校学习的主要语言。不过既然我们已经使用Python进行从大数据研究到量化策略编写的各种各样的工作,我们为什么不做得更好呢?





Python Zen中有这样一句:“There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. ”大意就是Python鼓励使用一种最优的方法去完成一件事,这也是和Ruby等的一个差异。所以一种好的Python编写习惯个人认为很重要,本文就重点从性能角度出发对python的一些惯用法做一个简单总结,希望对开发者有所帮助。

提到性能,最容易想到的是降低复杂度,一般可以通过测量代码回路复杂度(cyclomatic complexitly)和Landau符号(大O)来分析, 比如dict查找是O(1),而列表的查找却是O(n),显然数据的存储方式选择会直接影响算法的复杂度。


一、数据结构的选择:

     1. 在列表中查找:

   对于已经排序的列表考虑用bisect模块来实现查找元素,该模块将使用二分查找实现



而快速插入一个元素可以用:
bisect.insort(list, element)




这样就插入元素并且不需要再次调用 sort() 来保序,要知道对于长list代价很高.


    2. set代替列表:

    比如要对一个list进行去重,最容易想到的实现:



显然上面的实现的复杂度是O(n2),若改成:


seq = ['a', 'a', 'b']
res = set(seq)



复杂度马上降为O(n),当然这里假定set可以满足后续使用。

另外,set的union,intersection,difference等操作要比列表的迭代快的多,因此如果涉及到求列表交集,并集或者差集等问题可以转换为set来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大。


    3. 使用python的collections模块替代内建容器类型:

collections有三种类型:


deque:增强功能的类似list类型




defaultdict:类似dict类型




namedtuple:类似tuple类型




    列表是基于数组实现的,而deque是基于双链表的,所以后者在中间or前面插入元素,或者删除元素都会快很多。

     defaultdict为新的键值添加了一个默认的工厂,可以避免编写一个额外的测试来初始化映射条目,比dict.setdefault更高效,引用python文档的一个例子:
#使用profile stats工具进行性能分析


>>> from pbp.scripts.profiler import profile, stats
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3),
... ('blue', 4), ('red', 1)]
>>> @profile('defaultdict')
... def faster():
... d = defaultdict(list)
... for k, v in s:
... d[k].append(v)
...
>>> @profile('dict')
... def slower():
... d = {}
... for k, v in s:
... d.setdefault(k, []).append(v)
...
>>> slower(); faster()
Optimization: Solutions
[ 306 ]
>>> stats['dict']
{'stones': 16.587882671716077, 'memory': 396,
'time': 0.35166311264038086}
>>> stats['defaultdict']
{'stones': 6.5733464259021686, 'memory': 552,
'time': 0.13935494422912598}






可见性能提升了快3倍。defaultdict用一个list工厂作为参数,同样可用于内建类型,比如long等。

除了实现的算法、架构之外,Python提倡简单、优雅的编码风格。所以正确的语法实践又很有必要,这样才会写出优雅易于阅读的代码。


二、语法最佳实践:


1.字符串操作:优于Python字符串对象是不可改变的,因此对任何字符串的操作如拼接,修改等都将产生一个新的字符串对象,而不是基于原字符串,因此这种持续的 copy会在一定程度上影响Python的性能:


        (1)用join代替 '+' 操作符,后者有copy开销;

        (2)同时当对字符串可以使用正则表达式或者内置函数来处理的时候,选择内置函数。如str.isalpha(),str.isdigit(),str.startswith(('x','yz')),str.endswith(('x', 'yz'));

        (3)字符格式化操作优于直接串联读取:




str = "%s%s%s%s" % (a, b, c, d)  # efficient
str = "" + a + b + c + d + ""  # slow






2. 善用list comprehension(列表解析)  & generator(生成器) & decorators(装饰器),熟悉itertools等模块:
       (1) 列表解析,很多开发者觉得是Python2中最让人印象深刻的特性,举例1:



   举例2:




列表解析就是这么优雅简单。

   (2) 生成器表达式在Python2.2引入,它使用'lazy evaluation'思想,因此在使用内存上更有效。引用Python核心编程中计算文件中最长的行的例子:



这种实现简洁而且不需要把文件的所有行读入内存。

       (3) Python在2.4引入装饰器,又是一个让人兴奋的特性,简单来说它使得函数和方法封装(接收一个函数并返回增强版本的函数)更容易阅读、理解。'@'符号是装饰器语法,你可以装饰一个函数,记住调用结果供后续使用,这种技术被称为memoization的,下面是用装饰器完成一个cache功能:




运行结果:
6
we got a winner
6
4
4


装饰器在很多场景用到,比如参数检查、锁同步、单元测试框架等,有兴趣的开发者可以自己进一步学习。


三、 编码小技巧:


在Python3之前版本使用xrange代替range,因为range()直接返回完整的元素列表而xrange()在序列中每次调用只产生一个整数元素,开销小。(在Python3中xrange不再存在,里面range提供一个可以 遍历任意长度的范围的iterator)

if done is not None比语句if done != None更快;

尽量使用"in"操作符,简洁而快速:
for i in seq:
    print i

'x < y < z'代替'x < y and y < z';

while 1要比while True更快, 因为前者是单步运算,后者还需要计算;

尽量使用build-in的函数,因为这些函数往往很高效,比如add(a,b)要优于a+b;
在耗时较多的循环中,可以把函数的调用改为内联的方式,内循环应该保持简洁。

使用多重赋值来swap元素:


x, y = y, x  # elegant and efficient




而不是:


     temp = x
      x = y
      y = temp




三元操作符(python2.5后):V1 if X else V2,避免使用(X and V1) or V2,因为后者当V1=""时,就会有问题。

Python之switch case实现:因为switch case语法完全可用if else代替,所以Python就没  有switch case语法,但是我们可以用dictionary或lamda实现:

dictionary实现:

lambda实现:

用try…catch来实现带Default的情况,个人推荐使用dict的实现方法。

    这里只总结了一部分python的实践方法,希望这些建议可以帮助到每一位使用Python的开发者,不仅能优化性能,还能高效解决问题,让自己写的代码更加易于维护,更加Pythonic!
— — — — — — E N D — — — — — —
往期文章:
Numpy处理tick级别数据技巧
真正赚钱的期权策略曲线是这样的
多品种历史波动率计算
如何实现全市场自动盯盘
AI是怎样看懂研报的
真格量化策略debug秘籍
真格量化对接实盘交易
常见高频交易策略简介

如何用撤单函数改进套利成交

Deque提高处理队列效率

策略编程选Python还是C++

如何用Python继承机制节约代码量

十大机器学习算法
如何调用策略附件数据

如何使用智能单

如何扫描全市场跨月价差

如何筛选策略最适合的品种

活用订单类型规避频繁撤单风险

真格量化回测撮合机制简介

如何调用外部数据

如何处理回测与实盘差别

如何利用趋势必然终结获利

常见量化策略介绍

期权交易“七宗罪”

波动率交易介绍

推高波动率的因素

波动率的预测之道

趋势交易面临挑战
如何构建知识图谱
机器学习就是现代统计学

AI技术在金融行业的应用

如何避免模型过拟合

低延迟交易介绍

架构设计中的编程范式

交易所视角下的套利指令撮合

距离概念与特征识别

气象风险与天气衍生品

设计量化策略的七个“大坑”

云计算在金融行业的应用

机器学习模型评估方法
真格量化制作期权HV-IV价差
另类数据介绍

TensorFlow中的Tensor是什么?

机器学习的经验之谈

用yfinance调用雅虎财经数据

容器技术如何改进交易系统
Python调用C++
如何选择数据库代理
统计套利揭秘
[h1]一个Call搅动市场?让我们温习一下波动率策略[/h1][h1]如何用真格量化设计持仓排名跟踪策略[/h1][h1]还不理解真格量化API设计?我们不妨参考一下CTP平台[/h1][h1]理解同步、异步、阻塞与非阻塞[/h1][h1]隐波相关系数和偏度——高维风险的守望者[/h1]Delta中性还不够?——看看如何设计Gamma中性期权策略
[h1]Python的多线程和多进程——从一个爬虫任务谈起[/h1]线程与进程的区别皮尔逊相关系数与历史K线匹配
Python2和Python3的兼容写法Python代码优化技巧
理解Python的上下文管理器
如何写出更好的Python代码?这是Python软件基金会的建议
评估程序化模型时我们容易忽视的指标
看看如何定位Python程序性能瓶颈
什么是Python的GIL
投资研究中的大数据分析趋势及应用
理解CTP中的回调函数
如何围绕隐含波动率设计期权交易策略                    
看看如何用Python进行英文文本的情感分析
算法交易的分类



真格量化可访问:
https://quant.pobo.net.cn


真格量化微信公众号,长按关注:

遇到了技术问题?欢迎加入真格量化Python技术交流QQ群  726895887






















分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:870
帖子:180
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP