管理标准 Q/ZR
中兴软创科技股份有限公司企业标准
Q/ZR GJ 04.002.1-2016
ZSmart OCS Python脚本编写规范
V1.3
2016-11-14发布 2016-11-14实施
前 言
本文规定了ZSmart OCS Python脚本编写规范。
本文件评审、修正记录
文件名称 | 数据库模型设计及脚本管理流程 | |||
---|---|---|---|---|
序号 | 流程 | 拟制人/审核人 | 拟制/审核时间 | 版本号 |
1 | 编写 | 王宗旺 | 2016/11/14 | V1.0 |
2 | 更新,补充python版本说明 | 王宗旺 | 2016/11/21 | V1.1 |
3 | 更新,禁止使用eval函数。 | 王宗旺 | 2017/09/29 | V1.2 |
4 | 目录结构的格式更新 | 房月明 | 2021/08/30 |
V1.3 |
5 | python规范条目所属的规范类型 | 臧瑞瑞 | 2021/10/29 | V1.4 |
1. 编写目的
本文用于描述Python脚本通用规范(包含交付使用规范和对界面Python编译功能,以及业务代码)和ZSmart资费脚本使用规范。
2. 适用范围
本规范适用于基于Python2.7脚本的编写和使用,此类脚本用于ZSmart资费和充值订购赠送相关的计算和处理,ZSmart OCS/cvBS产品(V81系列,V9系列)目前建议使用Python 2.7版本。其他已经不在产品线演进计划中的,就需要项目自行决定是否进行改造。
3. 定义、缩写词及术语
专用的术语、定义见下表:
术语/定义 | 英文 | 说明 |
---|---|---|
python | python | 脚本语言 |
强制 | 要求必须满足,不满足就很有可能会产生故障 | |
建议 | 根据项目经验,这样可能会存在风险,建议修改 | |
最佳实践 | 根据项目经验,一些最佳实践的情况 | |
人工检查 | ||
界面python编译 | ||
体检工具 |
4. ZSmart OCS python脚本编写规范
4.1 格式
4.1.1 【建议】使用冒号(:)和代码缩进形式定义语句块
如:
def main(r):
eff_time = r.event.GetAttr(SERVICE_EFF_DATE).AsString()
体检工具(需要)
界面python编译(不需要)
4.1.2 【建议】不允许单行有多个表达式
一行只能表达一个含义。
如:
new_flag='A'
term_flag='A'
normal_flag='A'
以下就是不推荐的情况:
new_flag='A'; term_flag='A'; normal_flag='A'
4.1.3 【建议】行首使用空格缩进
行首使用前导空格缩进,不使用tab,禁止tab和空格混用。
正确:
eff_time = r.event.GetAttrEx(SERVICE_EFF_DATE).AsString()
4.1.4 【建议】每级缩进使用四个空格符号
比如:
if(created_date >= cycle_begin_time and created_date <= cycle_end_time):
# not charge
if(new_flag == "N"):
charge = 0
# charge per day
elif(new_flag == "A"):
d1 = diffdays(cycle_end_time,completed_date)
charge = d1 * price_day
# charge per cycle
else:
charge = price_cycle
体检工具(需要)
界面python编译(不需要)
4.1.5 【建议】变量名须有意义
变量必须有含义,长度要简洁,建议不要超过20个字符。
*人工检查
4.1.6 【强制】使用GetAttrEx()函数取代GetAttr()函数
V8以及V8以后的版本,不再使用GetAttr()函数,要求使用GetAttrEx()函数获取相关参数。
以下有异常情况,需要各个项目去排查,有用多个参数的时候,就需要改造到GetAttrEx中去。
关于 Python中的 GetAttr和GetAttrEx函数,遇到个情况,就是需要传多个参数的话,只能用 GetAttr, GetAttr(ACCT_RES_CHARGE,1,账户id, 订户id), GetAttrEx只能有一个入参,要求产品功能对GetAttrEx扩展,支持多参数的情况,达到替换GetAttr的目的。
由于老的代码的问题,所以,初期只是业务代码发现这个就告警,提示用户
体检工具(需要)
界面python编译(先告警)
4.1.7 【强制】增加参数需向下兼容
开发为已有的Python函数增加参数的时候,必须要保证向下兼容,不设置参数的时候逻辑和之前相同。
*产品开发规范
4.1.8 【建议】Python脚本取当前时间建议使用属性EVENT_BEGIN_TIME
一些代码中向915属性中填写的时间不正确,导致从扩展属性915中获取当前时间错误。
*人工检查
4.1.9 【强制】使用打印日志代替抛异常
资费的python脚本不能抛异常,资费中的python脚本使用打印输出告警日志代替异常。
体检工具(需要)
界面python编译(需要)
4.1.10 【强制】分拣和高级触发器中python不能包含event
分拣和高级触发器中可以添加python,对函数的使用方式有要求
正确:cycle_info = r.GetBillingCycleInfo()
错误:cycle_info = r.event.GetBillingCycleInfo()
4.2 Python效率
4.2.1 【建议】使用空行分割代码块以增强可读性
代码过多的时候建议分块,每块不宜超过10行。
代码块的首行需要有注解,说明本代码块的功能。
*人工检查
4.2.2 【建议】注解行以”#”加若干空格开始
注释必须保存在表达式上一行,和下一行代码对齐。
不建议的注释方式:
billingcycleid = r.event.GetAttr(22).AsInteger() # get current billing cycle id
建议改为:
# get current billing cycle id
billingcycleid = r.event.GetAttr(BILLING_CYCLE_ID).AsInteger()
4.2.3 【建议】单行长度不超过120个字符
4.2.4 【建议】使用內建函数(abs, len, int等)
建议使用内建函数(abs,len,int等)代替自己编写的函数提升性能
在字符串处理和时间等运算中,优先使用python内置的函数
*人工检查
4.2.5 【建议】使用join()函数
4.2.6 【建议】合并判断条件
建议在条件判断中使用”in”代替“or”和“else”,合并判断,提升性能
推荐:
if(-res in(0,3600,14500,24800,33000,35000,55500,78600,111100,200000,300000)):
res=0
不推荐:
if(-res==0 or –res==3600 or –res==14500):
res=0
elif(-res==14500 or -res==24800 or -res==33000):
res=0
4.2.7 【建议】使用CODE而不用ID
直接使用ID可读性不好,不易维护,建议使用编码代替ID,比如EVENT_BEGIN_TIME代替3,BILLING_CYCLE_ID代替22,这样可读性更好一些。如果必须使用ID,必须注释说明含义和来源。
正确:
billingcycleid = r.event.GetAttrEx(BILLING_CYCLE_ID).AsInteger()
错误:
billingcycleid = r.event.GetAttrEx(22).AsInteger()
4.2.8 【强制】适度的print操作,不超过5行
建议适度的print操作
项目在调试阶段会调用print和printlog()打印日志,性能测试表明PYTHON中是否有Print语句有5%的性能差异,所以建议在上线后屏蔽一些print操作,只保留必要的信息,生产环境的print操作建议不超过5行。
4.2.9 【最佳实践】禁止使用eval函数
我们的python脚本习惯使用eval函数来获取916属性, 而eval函数也是通过动态构造class来实现,项目在某些场景下使用python的eval函数获取计算结果,导致产生大量动态class,gc耗时增加,从而导致ussd订购超时。
实验证明gc耗时的增长与eval函数有关,建议通过java来实现,避免使用eval函数。
*工具检查
4.2.10 【建议】减少for循环的使用
对性能影响较大,尽量减少for循环的使用
4.3 注意项
4.3.1 【建议】禁止使用中文注解
比如以下的注解不符合规范:
# This python is used for topup reward,支持V7.3a版本
体检工具(需要)
界面python编译(不需要)
4.3.2 【建议】常量名全部大写
常量名所有字母大写,由下划线连接各个单词,严禁大小写混用
*人工检查
4.3.3 【建议】变量名全部小写
变量名全部小写,由下划线连接各个单词,严禁大小写混用
正确:
prod_state = r.event.GetAttr(PROD_STATE).AsString()
错误:
Prod_State = r.event.GetAttr(PROD_STATE).AsString()
4.3.4 【建议】函数名全部小写
函数名全部小写,由下划线连接各个单词,严禁大小写混用(同变量名)
*产品开发规范
4.3.5 【强制】禁止在python中使用import
禁止在python中使用import,如有需要提单给开发处理
有项目现场配置的Python脚本导入sys库,遇到异常数据时直接exit导致进程退出。还有项目导入time库调用strptime,difftime等函数用于处理时间运算和格式转换,导致递归死循环。原则上现场的python脚本不应该import,可在前台界面上增加关键字检测。
4.3.6 【强制】禁止调用sys.exit()函数退出
不能调用sys.exit()函数退出,sys模块的exit()函数会导致业务进程退出
体检工具(需要)
界面python编译(需要)
4.3.7 【强制】所有新版本需要兼容老版本的函数
Python函数理论上应该是需要向下兼容,从v70升级到v73,从v73升级到v81,python脚本不能直接到新版本使用,主要就是这两个版本的python脚本有些属性ID、函数等变化,导致现场要修改相关的Python脚本,增加割接难度和错误几率,升级工作量太大。
*立项结项评审
4.4 最佳实践
4.4.1 【最佳实践】需对函数返回值做判断
所有的变量作为函数返回值,需要添加有效性判断。
eff_time = r.event.GetAttrEx(SERVICE_EFF_DATE).AsString()
if(eff_time …)
elif (eff_time …)
else
由于GetAttrEx等函数返回的字符串不一定是标准的格式,在找不到数据等情况下,会输出非标准的数据格式,脚本需要做一个有效性判断,才能继续用于逻辑判断和业务处理。有项目现场的python脚本中调用GetProdCompletedDate函数返回了NULL,而未做出错处理,直接用这个返回值调用diffdays导致了业务进程core。
要求研发提供所有的函数的规格说明。
*工具检查
4.4.2 【最佳实践】可以使用long代替int
可能入参长度较长,会导致int()不支持,可以使用long()代替,我们公司系统用到的是python 2.7,支持long函数使用
*人工检查
4.4.3 【最佳实践】多个同层级的条件判断时,使用elif代替if
对于同层级的条件,需要分开判断时,建议使用elif代替if, 按照可能性大小进行排序,可能性最大的,放在前面,提高效率。
建议:
a = 1
if a < 2:
print(2)
elif a < 3:
print(3)
else:
print(1)
不建议:
a = 1
if a < 2:
print(2)
if a < 3:
print(3)
else:
print(1)
*人工检查