管理标准 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 【建议】使用冒号(:)和代码缩进形式定义语句块

如:

  1. def main(r):
  2. eff_time = r.event.GetAttr(SERVICE_EFF_DATE).AsString()

体检工具(需要)
界面python编译(不需要)

4.1.2 【建议】不允许单行有多个表达式

一行只能表达一个含义。
如:

  1. new_flag='A'
  2. term_flag='A'
  3. normal_flag='A'

以下就是不推荐的情况:

  1. new_flag='A'; term_flag='A'; normal_flag='A'

体检工具(需要)
界面python编译(不需要)

4.1.3 【建议】行首使用空格缩进

行首使用前导空格缩进,不使用tab,禁止tab和空格混用。
正确:

  1. eff_time = r.event.GetAttrEx(SERVICE_EFF_DATE).AsString()

体检工具(需要)
界面python编译(不需要)

4.1.4 【建议】每级缩进使用四个空格符号

比如:

  1. if(created_date >= cycle_begin_time and created_date <= cycle_end_time):
  2. # not charge
  3. if(new_flag == "N"):
  4. charge = 0
  5. # charge per day
  6. elif(new_flag == "A"):
  7. d1 = diffdays(cycle_end_time,completed_date)
  8. charge = d1 * price_day
  9. # charge per cycle
  10. else:
  11. 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()

体检工具(需要)
界面python编译(需要)

4.2 Python效率

4.2.1 【建议】使用空行分割代码块以增强可读性

代码过多的时候建议分块,每块不宜超过10行。
代码块的首行需要有注解,说明本代码块的功能。
*人工检查

4.2.2 【建议】注解行以”#”加若干空格开始

注释必须保存在表达式上一行,和下一行代码对齐。
不建议的注释方式:

  1. billingcycleid = r.event.GetAttr(22).AsInteger() # get current billing cycle id

建议改为:

  1. # get current billing cycle id
  2. billingcycleid = r.event.GetAttr(BILLING_CYCLE_ID).AsInteger()

体检工具(需要)
界面python编译(不需要)

4.2.3 【建议】单行长度不超过120个字符

体检工具(需要)
界面python编译(不需要)

4.2.4 【建议】使用內建函数(abs, len, int等)

建议使用内建函数(abs,len,int等)代替自己编写的函数提升性能
在字符串处理和时间等运算中,优先使用python内置的函数
*人工检查

4.2.5 【建议】使用join()函数

建议使用join()函数代替+连接字符串提升性能
*人工检查

4.2.6 【建议】合并判断条件

建议在条件判断中使用”in”代替“or”和“else”,合并判断,提升性能
推荐:

  1. if(-res in(0,3600,14500,24800,33000,35000,55500,78600,111100,200000,300000)):
  2. res=0

不推荐:

  1. if(-res==0 or res==3600 or res==14500):
  2. res=0
  3. elif(-res==14500 or -res==24800 or -res==33000):
  4. res=0

*人工检查

4.2.7 【建议】使用CODE而不用ID

直接使用ID可读性不好,不易维护,建议使用编码代替ID,比如EVENT_BEGIN_TIME代替3,BILLING_CYCLE_ID代替22,这样可读性更好一些。如果必须使用ID,必须注释说明含义和来源。
正确:

  1. billingcycleid = r.event.GetAttrEx(BILLING_CYCLE_ID).AsInteger()

错误:

  1. billingcycleid = r.event.GetAttrEx(22).AsInteger()

*人工检查

4.2.8 【强制】适度的print操作,不超过5行

建议适度的print操作
项目在调试阶段会调用print和printlog()打印日志,性能测试表明PYTHON中是否有Print语句有5%的性能差异,所以建议在上线后屏蔽一些print操作,只保留必要的信息,生产环境的print操作建议不超过5行。

体检工具(需要)
界面python编译(需要)

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 【建议】变量名全部小写

变量名全部小写,由下划线连接各个单词,严禁大小写混用
正确:

  1. prod_state = r.event.GetAttr(PROD_STATE).AsString()

错误:

  1. 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,可在前台界面上增加关键字检测。

体检工具(需要)
界面python编译(需要)

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 【最佳实践】需对函数返回值做判断

所有的变量作为函数返回值,需要添加有效性判断。

  1. eff_time = r.event.GetAttrEx(SERVICE_EFF_DATE).AsString()
  2. if(eff_time …)
  3. elif (eff_time …)
  4. 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, 按照可能性大小进行排序,可能性最大的,放在前面,提高效率。
建议:

  1. a = 1
  2. if a < 2:
  3. print(2)
  4. elif a < 3:
  5. print(3)
  6. else:
  7. print(1)

不建议:

  1. a = 1
  2. if a < 2:
  3. print(2)
  4. if a < 3:
  5. print(3)
  6. else:
  7. print(1)

*人工检查