我爱编程

ylib代码库指引(1225更新 )

2018-08-01  本文已影响0人  唯此

背景

程序设计好之后,写的具体代码核心要满足可读性.方便未来的debug与修改功能.本规范试图把程序设计领域的经验智慧与团队的具体实际相结合.

for-if-else不能超过三层

Flat is better than nested. --The Zen of Python

def get_page_amount(self) {
  if (is_dead()): 
      return self.dead_amount();
  if (is_separated()): 
      return self.separated_amount();
  return self.normal_pay_amount();
}; 
if is_comment():
     do_something()
else:     # 改为 elif: is_code(): do_something2(); else: do_something3()
     if is_code():
          do_something2()
     else
          do_something3()
}; 
even_list = []
for i in int_list:
    if i is None:
         continue
    elif i % 2 == 0:
        even_list.append(i)
int_frm = pd.DataFrame(columns=['int'], data=int_list)
even_frm = int_frm[int_frm['int'] % 2 == 0]
注释
class SmartBoy()
"""初始化需要iq变量"""
    def find_girl(self, place):
    """place需要时一个地址"""
if a>b and c<2: #代表居中的情况
    do_something()
if is_center(): 
    do_something()

注释的写法可以参考pandas
以及:https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.htm

try-except中的try语句不超过两行
print 规范

print必须包含name, 这样知道print是从哪里来的.

print('{}: start!'.format(__name__))
默认参数不能是可变对象

任何函数的默认参数都不可以是可变对象,以下默认参数的写法是错的.这样会导致python程序重复创建等问题.

def __init__(self, io_behavior=IoBehavior(),a_dict = dict()):

可以写为:

def __init__(self, io_bhv=None):
    if io_bhv is None:
        self.io_bhv= IoBhv()

或使用ensure_class 函数简化代码.

from helper import ensure_class # 从ylib.helper导入
# 如果a_var为None的话,会返回IoBhv(), 不为None的时候会检查io_bhv是否属于IoBhv类型.
self.frm = ensure_class(io_bhv, IoBhv)
判断语句

frm中的None

如果frm中某一列是正整数,需要使用yfrm.INT_NONE来表示None的含义.如果直接使用python自带的none会把整个一列都变成为浮点类型.
如果frm中某一列的有效值允许为负整数,要么确保所有的数都大于-999,要么直接转化为浮点数来表示.

for循环中,不允许改动迭代对象
for i in list1:
    list1.pop() # 改动了list1,不允许.
如果本.py文件中存在中文字符,需要声明编码

#coding=utf-8

不允许出现重复3遍的代码组团

一旦发现这个确实是同一个逻辑重复了3遍.应该重构为一个方法或函数. 平常可以积累自己常用的重构方法.参考下面的重构方法:

py_codeline_dict[IMPORT_NUM] = len(import_lines)
py_codeline_dict[CLASS_NUM:class_lines] = len(class_lines)
# 后面还有好几行
cal_dict = {IMPORT_NUM: import_lines, CLASS_NUM:class_lines}
py_codeline_dict = {}
for key_i in cal_dict:
    py_codeline_dict[key_i] = len(cal_dict[key_i])

所有的requests都要设置time_out参数.

避免程序卡死,也方便在超时的时候发现问题所在.一般为5秒.

使用的所有常量都要集中管理

一行代码不要过长

不要超过pycharm给的推荐限制. 此时需要分行。

每个方法的代码不允许超过15行

不包括assert以及raise语句。可以通过提取结构, 优化语句等方式实现。

命名规范

[数据命名]

[pickle文件后缀名]

[文件名,变量名与类名<16个字符]

[不允许出现高度相似的名称]
典型如只有一个字符的区别: divc_tag之于 divct_tag, sse_worker 之于 sze_worker
实在无法避免可以把字符重复两遍,例如div_tt_frmdiv_cc_frm

[定义好概念]

基本类型的临时变量的值通常不允许变更

a = 89
b = a +3
a = 12 # 不想直接变更值,可以改为c = 12

warnings

避免代码在执行中因为使用了外部库不建议的用法而出现warnings

设计规范

[状态码]

类名规范

类方法规则

class Duck:
def __init__(self, name):
    self.name = name
def fly(self, wings):
    self.wings = wings
class Duck:
def __init__(self, name, wings):
    self.name = name
    self.wings = wings
def fly(self):
    print(self.wings)
class Duck:
def __init__(self, name):
    self.name = name
def fly(self):
    print(self.name)
​
class RedDuck(Duck):
def fly(self, height):
    print(height)
class Duck:
    def fly(self):
       raise NotImplementedError
class RedDuck:
    def fly(self):
       print("[RED_DUCK]I can fly!")
def get_apple(self):
"""[秘方] 获取苹果"""
class Duck:
    def __init__(self, where, time, height, type, weight, color):
       # N行赋值语句
    def fly(self):
        # 利用实例属性完成飞行任务
class Duck:
    def __init__(self, type, weight, color): #与这个类本性有关的参数放在这里,仅仅与单次行为有关的放在方法的参数里.
       # N行赋值语句
    def fly(self,where, time, height):
        # 利用实例属性以及本次方法的参数完成飞行任务

日期参数

--------------------------------------2018-08-10 新增--------------------------------------

is_方法.

函数或方法如果返回的值为Ture or False, 那么以is作为前缀

do_方法

执行类的多步操作的方法要以do为前缀.

class Worker():
    def action_1(self):
        # some code
    def action_2(self):
        # some code
    def do_job(self):
        self.action_1()
        self.action_2()

Bhv,Stp后缀,

行为类有Bhv后缀, 步骤类有Stp后缀.

Base前缀

常数定义

常数通常放在类定义下方定义,多个类要共享的常数放在config.py里面.
--------------------------------------2018-08-11 新增--------------------------------------

.py文件分隔

-原则上一个步骤类来与它依赖的行为类放在一个.py文件里面. 如果有多个步骤类有共同依赖的行为类,那么这个行为类可以单独拆分出来放在bhv.py文件里.

类的参数不可以为文件路径

class md2line():
def get_code_lines.do(the_path)
      res = io_state.read(the_path)# 引入了不必要的依赖,而且调用者无法直接传递res参数
def get_code_lines.do(the_frm)# 规范, 参数从路径变为数据

不允许在类里面新建其他类实例

class RedDuck:
    def __init__(self):
       self.fly_bhv = FlyBehavior()
    def fly(self):
        self.fly_bhv.fly()
class RedDuck:
    def __init__(self, fly_behavior):
       self.fly_behavior = fly_behavior
    def fly(self):
        self.fly_behavior.fly()
类型断言

Errors should never pass silently. -- The Zen of Python

assert isinstance(a_var, pd.DataFrame)
if deal_frm():
    assert isinastance(res, pd.DataFrame)
    return res
elif deal_list():
    assert isinastance(res, list)
    return res
else:
    return 0 # 直接return一个常数的话不需要保证类型.
def read(file_path):
     if os.path.exists(file_path): # 不满足的话会返回None
        return self.read_pickle(file_path)
def read(file_path):
     if os.path.exists(file_path): # 不满足的话会直接报错
        return self.read_pickle(file_path)
      raise ValueError

__init__方法自定义实例变量不应该超过2个.

如果发现自己代码中init方法中的实例变量超过两个,那么可以采用某个中间类来封装这些类变量.

def __init__(HtmlIo, IoBhv, FtpBhv) # 需要重构,可以使用状态模式封装,或者用工厂模式封装类的创建.

2018-08-13新增

引入第三方依赖

所有的第三方依赖库都要从yfack中引入(除了pandas).确保大家的版本都能够统一. 也能够节省大家写import的时间.范例:

from ypack import np, es, requests

注释里面要写数据结构的样式

[类别标志]

BLOCK_CMNT = 20
INLINE_CMNT = 21
CODE_LINE = 30
if is_block_comment():
    the_type =  'block_comment'  #这些常数散落在程序各处,未来很难以管理
elif is_inline_comment():
    the_type = 'inline_comment'
INLINE_COMMNET = 'inline_comment' #这些常数通常集中在一起
BLOCK_COMMNET = 'block_comment'
# other codes...
if is_block_comment():
    the_type = BLOCK_COMMNET 
elif is_inline_comment():
    the_type = INLINE_COMMNET 

2018-08-17新增

  1. 每个类至少有2个或以上的方法(继承的方法不算,含有__init__方法).
    如果类过小,可以考虑与其他的类合并或者把父类某些参数做成可配置的.参考下面的重构:
class BaseSpider():
HEADERS = "abc"
// other codes

class SHSpider():
HEADERS = "bcd"
class BaseSpider():
HEADERS = "abc"
def change_paras(headers):
    self.HEADERS = headers #这样就不需要SHSpider类了.

2018-12-25新增
【datadesign规范】
1.每次项目之前都要设计数据结构。需要有一个data_design.py文件放上所有input与output数据的schema。
2.参考ylib\helper\tests\test_yassert.py中的方式判定数据结构。dataframe的数据格式判定可以通过把它转化为dict之后再判定(或者直接自己写helper函数)
2.data_design.py有任何变动,需要在早会与下午会中讨论。

上一篇 下一篇

猜你喜欢

热点阅读