python 代码规范

python代码规范–如何写出优雅、简洁并易于理解的代码

首部信息

首先在创建文件时,应该在文件首部声明一些必要信息,如开发者姓名、开发者联系方式以及该脚本的功能描述等,这样便于后续脚本的维护和审查。

若使用pycharm开发工具,可以在File-->settings-->Editor-->FIle and Code Templates中设置,这样每次新建文件时会自动生成某些基础信息。

基础信息模板:

1
2
3
4
5
6
7
# _*_ coding:utf-8 _*_
"""
@time :${DATE} ${TIME}
@author :liutengfei
@mail:liutengfei@bertadata.com
@desc:
"""

面向对象Class

  • 类名使用驼峰式命名方法,类名应与脚本文件名一致
  • 对于无须继承任何类(或者说继承于基类object)的类,使用classs ClassName:而非class ClassName(object):定义

  • 对于类变量或全局变量,使用全大写英文单词命名,多个单词间使用_连接,最多不超过3

    ps: 类变量指的是定义在类中,但在各个类方法外的变量。类变量的特点是:所有类的实例化对象都可以共享类变量的值,即类变量可以在所有实例化对象中作为公用资源。 类变量推荐直接用类名访问,但也可以使用对象名访问。

  • 使用全小写英文单词命名实例变量或普通变量,如果需要多个单词的,单词之间使用_连接,单个变量使用单词的个数不超过3

  • 私有变量/保护变量:_xxx单下划线开头叫保护变量,意思是只有类对象和子类对象自己能访问到这些变量,此变量不能通过from XXX import xxx 导入;__xxx 双下划线开头叫私有变量,只允许类本身访问,连子类对象也不能访问到这个数据

    ps:私有方法和保护方法的定义该规则同样适用

  • 使用self作为实例方法的第一个参数;使用cls作为类方法的第一个参数;当定义的方法在实现时并不需要引用类或者实例时,定义为静态方法,使用 装饰器@staticmethod 来定义

    ps:类方法较少使用,其具体使用场景有待总结

代码长度

一行代码最多不要超过79个字符

超出此限制时应该在元素周围使用()包围并拆行,而不是在行末添加\并换行

空行规定

  • 文件头部的引用声明与正式代码之间有两个空行
  • 类声明周围有两个空行
  • 类外部的函数周围有两个空行
  • 类外部的逻辑块,如条件、循环语句周围有两个空行
  • 类内部方法声明周围有一个空行
  • 类外部的变量周围有一个空行
  • 文件末尾添加一个空行

导入规定

  • 一行导入一个库里的文件,隶属于不同库的量不要在一行导入
  • 引用按照内置、第三方、本地库的顺序排序,各类引用之间有一个空行。

缩进

使用四个空格作为缩进;使用Space而不是Tab键输入缩进。建议将编辑器设置为一个Tab表示4个空格,并配置按Tab时插入空格,vscodeCtrl + ,

容器类型书写规范

列表,字典,元组等,首末元素与对应的包围符号之间、函数调用的括号周围没有空格
容器类型尾元素最后没有,”,各元素之间的“,”后面有一个空格

糟糕:

1
2
3
4
5
6
7
bad_list = [ 1,2, ]

tuple_a = ( 1,2, )

bad_dict = { 'a':1,'b':2, }

bad_function_call( 1, 2 )

优雅:

1
2
3
4
5
6
7
8
9
10
11
12
13
good_list = [1, 2]

good_tuple = (1, 2)

# 只有单个元素的元组末尾的逗号是必须的,需注意
good_tuple = (1,)

good_dict = {
'a': 1,
'b': 2
}

good_function_call(1, 2)

符号及空格

  • 逗号,之后要有一个空格,示例见上文

  • 字典的冒号之后有一个空格;列表切片的冒号之间没有空格

  • 操作符,如+, -, *, /,=, ==, +=周围有一个空格

函数规定

必须使用函数注解

函数注解的使用十分简单并且有利于所有人——包括自己对于程序的理解

示例

1
2
3
4
5
6
7
8
from bs4 import BeautifulSoup


def bdd(a: int, b: int=2) -> int:
return a + b

def parse_content(sp: BeautifulSoup) -> list:
return sp.select('css selector')

函数文档字符串

对于简单函数,判断标准为参数少于等于三个,不需要;复杂函数需要使用文档字符串并包含以下内容
对于提供给所有人使用的公共方法,文档字符串中必须提供调用示例返回值示例

  • 函数的作用
  • 各参数的作用及其限制
  • 调用示例

函数默认参数的=号周围没有空格

如果函数返回None无需指明返回值类型;函数末尾的return语句,不需要

函数长度:如果一个函数一个屏幕放不下,建议拆分,这里没有固定长度,酌情而定,原则是一个函数不要过于复杂。

使用对象自身的真值

糟糕

1
2
3
4
5
6
7
8
9
empty_list = []

if len(empty_list) == 0:
pass

a = None

if a is None:
pass

优雅

1
2
3
4
5
6
7
8
9
empty_list = []

if empty_list:
pass

a = None

if a:
pass

语句使用规定

合理使用try语句块。try语句仅仅只用于可能抛出异常的语句,不要用其监控一大段代码,将正常执行的其它代码放在else语句中

糟糕

1
2
3
4
5
6
7
8
9
10
from json import loads


json_str = 'json字符串'

try:
json_data = loads(json_str)
# 对json_data做一些解析操作
except:
pass

优雅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
json_str = 'json字符串'

def parse_data(json_str: str)
try:
# 这句是唯一可能抛出异常的地方,捕获此处的异常即可
json_data = loads(json_str)
except JSONDecodeError as _e:
# 解析json失败,后面也没有继续的必要
return print(_e)
else:
# 对json_data做一些操作
pass
finally:
# 做一些最终操作
# 即使except或else语句里有return,这里的内容也会执行

使用continuebreak等语句搭配提前返回

示例

1
2
3
4
5
6
7
for each in range(10):
if each < 4:
continue
if each > 8:
break
# 以下为符合条件的元素处理逻辑
pass

异常的使用

记住几点基础的即可,个人自定的异常默认都继承于Exception,异常只是一个名字,表示某种超出预期之外的情况

1
2
3
4
5
6
7
8
9
BaseException
|-- SystemExit
|-- KeyboardInterrupt
|-- GeneratorExit
|-- Exception
|-- StopIteration
|-- StandardError
|-- BufferErr
|-- OtherErr
  • 使用except Exception:会捕获绝大部分自定义异常,但不能捕获Ctrl + C触发的KeyboardInterrupt异常
  • 使用except:会捕获BaseException,这会拦截掉所有异常,不要使用

常见异常

1
2
3
4
5
6
ValueError: 如 int('hello')
IndexError:下标越界
TypeError:如遍历一个非可迭代的对象
KeyError:使用[]访问字典中不存在的键
AttributeError:访问对象中不存在的属性
JSONDecodeError:使用json.loads(),传入非json字符串

提前返回

提前返回是避免产生多层嵌套判断的重要技巧,主要方法就是不判断为真的条件而判断假的条件,满足假的情况下即返回

糟糕

1
2
3
4
5
6
def process(condition: str):
if isinstance(condition, str):
if 'asd' in condition:
if len(condition) > 4:
# 所有条件符合,开始处理逻辑
pass

优雅

1
2
3
4
5
6
7
8
9
def process(condition: str):
if not isinstance(condition, str):
return # 没有后续处理的必要
if 'asd' not in condition:
return
if len(condition) <= 4:
return
# 不符合条件的都已提前返回,开始正式处理
pass

_变量名

_代表程序中上句表达式的值,这通常是无用的

示例

1
2
3
4
5
6
7
# 使用for循环控制重试次数
for _ in range(3):
# do sth
pass

# 只取首末值,丢弃中间值
a, *_, b = range(10)

lambda匿名函数

基本语法: lambda argument_list: expression

这里的argument_list参数列表,它的结构与Python中函数(function)的参数列表是一样的;

这里的expression是一个关于参数的表达式。表达式中出现的参数需要在argument_list中有定义,并且表达式只能是单行的。

下面是一些lambda函数示例:

1
2
3
4
lambda x, y: x*y;函数输入是x和y,输出是它们的积x*y
lambda:None;函数没有输入参数,输出是None
lambda *args: sum(args); 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
lambda **kwargs: 1;输入是任意键值对参数,输出是1

高阶函数

一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数

  • map函数: 接收的是两个参数,一个函数,一个序列(可迭代对象),其功能是将序列中的值处理再依次返回至列表内;其返回值为一个迭代器对象;为python内置函数;

    示例:

    1
    2
    3
    4
    5
    list(map(lambda x:x.upper(),"amanda"))
    ['A', 'M', 'A', 'N', 'D', 'A']

    list(map(lambda x: x*x, range(10)))
    [0, 1, 4, 9, 16, 25, 36, 49, 64,81]
  • filter函数: filter函数也是接收一个函数和一个序列(可迭代对象),其主要功能是过滤。其返回值也是迭代器对象 ;为python内置函数;

    示例:

    1
    2
    3
    4
    5
    6
    names=["Alex","amanda","xiaowu"]
    list(filter(lambda x:x.islower(),names))
    ['amanda', 'xiaowu']

    list(filter(lambda x: x > 2, range(10)))
    [3, 4, 5, 6, 7, 8, 9]
  • reduce函数: reduce函数也是一个参数为函数,一个为一个序列(可迭代对象),其返回值为一个值而不是迭代器对象,故其常用与叠加、叠乘等 ;非内置函数,在模块functools中,需要导入;

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    nums = [1, 2, 3, 4, 5, 6]
    reduce(lambda x,y:x*y,nums)
    720

    reduce(lambda x,y:x*y,nums,100)
    72000

    reduce(lambda x,y:x+y,nums)
    21

    reduce(lambda x,y:x+y,nums,100)
    121

    注:reduce还支持第三个参数,其作为叠加或叠乘的基数

  • sorted函数 :python内置排序函数,第一个参数为序列(可迭代对象),第二个参数为映射函数(可自定义),第三个可选参数为reverse(逆序);

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sorted([36, 5, -12, 9, -21])
    [-21, -12, 5, 9, 36]

    sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]

    sorted(['bob', 'about', 'Zoo', 'Credit'])
    ['Credit', 'Zoo', 'about', 'bob'] # 默认按ASCII值排序的

    sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) # 不区分大小写
    ['about', 'bob', 'Credit', 'Zoo']

    sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
    ['Zoo', 'Credit', 'bob', 'about']
  • any函数: any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True;python内置函数;

    示例:

    1
    2
    3
    4
    any([False, '', [], None, {}, (), 0])
    False
    any([False, '', [], None, {}, (), 1])
    True

    注:仅支持一个参数

  • all函数: all() 函数用于判断给定的可迭代参数iterable 中的所有元素是否都为 True,如果是返回 True,否则返回 False ;python内置函数;

    示例:

    1
    2
    3
    4
    5
    all([False, '', [], None, {}, (), 1])
    False

    all([True, 'abc', ['1'], {'a': 'b'}, (1, 1), 1])
    True