• python通用规范-4


    文章目录

    4.1 `None`值比较
    4.2 模块导入控制 __all__
    4.3 字典取值的推荐方式(`get`)
    4.4 列表切边不推荐负步长值
    4.5 参数的类型检查推荐`isinstance`
    4.6 使用列表推导式替换循环
    4.7 功能代码应该封装在函数或类中
    4.8 精确数值计算的场景使用`Decimal`模块
    4.9 避免对不同对象使用同一个变量名
    4.10 类方法的装饰
    4.11 使用包(`package`)形式管理不同目录下的源码
    4.12 避免在代码中修改`sys.path`列表
    4.13 使用枚举替代`range`
    4.14 避免重复定义变量名
    4.1 None值比较

    与None作比较要使用is或is not,不要使用等号

    说明:
    is判断是否指向同一个对象(判断两个对象的id是否相等),==会调用eq方法判断是否等价(判断两个对象的值是否相等)。
    示例:同一个实例,使用“is”和“==”的判断结果不同。

    >>> class Bad(object):
    def __eq__(self, other):
    return True
    >>> bad_inst = Bad()
    >>> bad_inst == None
    True
    >>> bad_inst is None
    False
    1
    2
    3
    4
    5
    6
    7
    8
    4.2 模块导入控制 all

    定义一个all不会把本模块的所有内容都暴露在外部,将允许外部访问的变量、函数和类的名字放进去

    说明:
    在模块中定义了__all__之后,从外部from module import *只会import __all__中定义的内容。
    示例:

    # sample_package.py
    __all__ = ["sample_external_function"]


    def sample_external_function():
    print("This is an external function..")

    def sample_internal_function():
    print("This is an internal function..")


    # main.py
    from sample_package import *

    if __name__ == "__main__":
    sample_external_function()
    sample_internal_function().

    NameError: name 'sample_internal_function' is not defined
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    4.3 字典取值的推荐方式(get)

    避免直接使用dict[key]的方式从字典中获取value,如果一定要使用,需要注意当key not in dict时的异常捕获和处理

    说明:
    Python的字典dict可以使用key获取其对应的value。但是当key在dict的key值列表中不存在时,直接使用dict[key]获取value会报KeyError,应当使用更为安全的dict.get(key)类型方法获取value。
    # 错误示例:
    sample_dict = {'default_key': 1}
    sample_key = 'sample_key'
    sample_value = sample_dict[sample_key]

    # 正确示例:
    sample_dict = {'default_key': 1}
    sample_key = 'sample_key'
    sample_value = sample_dict.get(sample_key)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    4.4 列表切边不推荐负步长值

    对序列使用切片操作时,不建议使用负步进值进行切片

    说明:
    Python提供了sample_list[start : end : stride]形式的写法,以实现步进切割,也就是从每stride个元素中取一个出来。但如果stride值为负,则会使代码难以理解,特定使用场景下还会造成错误。
    # 错误示例:
    # 如下写法,在start : end : stride都使用的情况下使用负的stride,会造成阅读困难。此种情况建议将“步进”切割过程和“范围”切割过程分开,使代码更清晰。
    >>> a = [1,2,3,4,5,6,7,8]
    >>> a[2::2]
    [3,5,7]
    >>> a[-2::-2]
    [7,5,3,1]
    >>> a[-2:2:-2]
    [7,5]
    >>> a[2:2:-2]
    []
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    4.5 参数的类型检查推荐isinstance

    传递实例类型参数后,函数内应使用isinstance函数进行参数检查,不要使用type

    说明:如果类型有对应的工厂函数,可使用它对类型做相应转换,否则可以使用isinstance函数来检测。使用函数/方法参数传递实例类型参数后,函数内对此参数进行检查应使用isinstance函数,使用is not None,len(para) != 0等其它逻辑方法都是不安全的。
    # 错误示例:
    # 下面的函数保护未能完成应有的检查功能,传入一个tuple就可以轻易绕过保护代码造成执行异常。
    >>> def sample_sort_list(sample_inst):
    ... if sample_inst is []:
    ... return
    ... sample_inst.sort()
    >>> fake_list = (2, 3, 1, 4)
    >>> sample_sort_list(fake_list)
    Traceback (most recent call last):
    File "<pyshell#232>", line 1, in <module>
    sample_sort_list(fake_list)
    File "<pyshell#230>", line 4, in sample_sort_list
    sample_inst.sort()
    AttributeError: 'tuple' object has no attribute 'sort'

    # 正确示例:
    # 使用instance函数对入参进行检查,检查后可以按照需求raise exception或return。
    >>> def sample_sort_list(sample_inst):
    ... if not isinstance(sample_inst, list):
    ... raise TypeError(r"sample_sort_list in para type error %s" % type(sample_inst))
    ... sample_inst.sort()
    >>> fake_list = (2, 3, 1, 4)
    >>> sample_sort_list(fake_list)
    Traceback (most recent call last):
    File "<pyshell#235>", line 1, in <module>
    sample_sort_list(fake_list)
    File "<pyshell#234>", line 3, in sample_sort_list
    raise TypeError(r"sample_sort_list in para type error %s" % type(sample_inst))
    TypeError: sample_sort_list in para type error <type 'tuple'>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    4.6 使用列表推导式替换循环

    尽量使用推导式代替重复的逻辑操作构造序列。但推导式必须考虑可读性,不推荐使用两个以上表达式的列表推导

    说明:
    推导式(comprehension)是一种精炼的序列生成写法,在可以使用推导式完成简单逻辑,生成序列的场合尽量使用推导式,但如果逻辑较为复杂(> 两个逻辑表达式),则不推荐强行使用推导式,因为这会使推导式代码的可读性变差。
    # 错误示例:
    # 如下逻辑代码,逻辑较为简单,实现此逻辑的代码不但有循环,而且较为复杂,性能不佳。
    odd_num_list = []
    for i in range(100):
    if i % 2 == 1:
    odd_num_list.append(i)
    # 正确示例:
    odd_num_list = [i for i in range(100) if i % 2 == 1]
    # 简单逻辑使用列表推导式实现,代码清晰精炼。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    4.7 功能代码应该封装在函数或类中

    说明:在Python中, 所有的顶级代码在模块导入时都会被执行. 容易产生调用函数, 创建对象等误操作。所以代码应该封装在函数或类中。即使是脚本类的代码,也建议在执行主程序前总是检查 if __name__ == '__main__' , 这样当模块被导入时主程序就不会被执行.
    正确示例:
    def main():
    ...
    if __name__ == '__main__':
    main()
    1
    2
    3
    4
    5
    4.8 精确数值计算的场景使用Decimal模块

    说明:在Python中,注意不要用浮点数构造Decimal,因为浮点数本身不准确。
    # 错误示例:
    >>> from decimal import Decimal
    >>> getcontext().prec = 28
    >>> Decimal(3.14)
    Decimal('3.140000000000000124344978758017532527446746826171875')

    # 正确示例:
    >>> from decimal import Decimal
    >>> Decimal('3.14')
    Decimal('3.14')
    >>> getcontext().prec = 6
    >>> Decimal(1) / Decimal(7)
    Decimal('0.142857')
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    4.9 避免对不同对象使用同一个变量名

    说明: Python是弱类型语言,允许变量被赋值为不同类型对象,但这么做可能会导致运行时错误,且因为变量上下文语义变化导致代码复杂度提升,难以调试和维护,也不会有任何性能的提升。
    # 错误示例
    items = 'a,b,c,d' # 字符串
    items = items.split(',') # 变更为列表

    # 正确示例
    items = 'a,b,c,d' # 字符串
    itemList = items.split(',') # 变更为列表
    1
    2
    3
    4
    5
    6
    7
    4.10 类方法的装饰

    类的方法不需访问实例时,根据具体场景选择使用@staticmethod或者@classmethod进行装饰

    说明: 一般的类方法要接收一个self参数表示此类的实例,但有些方法不需要访问实例,这时分为两种情况:
    1、方法不需要访问任何成员,或者只需要显式访问这个类自己的成员。这样的方法不需要额外参数,应当用@staticmethod装饰。 在Python 3.X版本中,允许直接定义不含self参数的方法,并且允许不通过实例调用。但是一旦通过实例调用这个方法,就会因为参数不匹配而出错。 加上@staticmethod进行修饰,可以让Python解释器明确此方法不需要self参数,提前拦截问题,可读性也更好。

    # 错误示例:
    class MyClass:
    def my_func(): # 没有用@staticmethod修饰,通过实例调用会出错
    pass


    MyClass.my_func() # Python 3.X中允许,2.X中出错
    my_instance = MyClass()
    my_instance.my_func() # Python 3.X和2.X中都会出错


    # 正确示例:
    class MyClass:
    @staticmethod
    def my_func(): # 用@staticmethod修饰后,解释器会将其解析为静态方法
    pass


    MyClass.my_func() # OK
    my_instance = MyClass()
    my_instance.my_func() # OK,但是不推荐,容易和普通方法混淆。最好写成MyClass.my_func()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    2、方法不需要访问实例的成员,但需要访问基类或派生类的成员。这时应当用@classmethod装饰。装饰后的方法,其第一个参数不再传入实例,而是传入调用者的最底层类。 比如,下面这个例子,通过基类Spam的count方法,来统计继承树上每个类的实例个数:

    class Spam:
    numInstances = 0

    @classmethod
    def count(cls): # 对每个类做独立计数
    cls.numInstances += 1 # cls是实例所属于的最底层类

    def __init__(self):
    self.count() # 将self.__class__传给count方法


    class Sub(Spam):
    numInstances = 0


    class Other(Spam):
    numInstances = 0


    x = Spam()
    y1, y2 = Sub(), Sub()
    z1, z2, z3 = Other(), Other(), Other()
    x.numInstances, y1.numInstances, z1.numInstances # 输出:(1, 2, 3)
    Spam.numInstances, Sub.numInstances, Other.numInstances # 输出:(1, 2, 3)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    但是使用@classmethod时需要注意,由于在继承场景下传入的第一个参数并不一定是这个类本身,因此并非所有访问类成员的场景都应该用@classmethod。比如下面这个例子中,Base显式的想要修改自己的成员inited(而不是派生类的成员),这时应当用@staticmethod。

    # 错误示例:
    class Base:
    inited = False
    @classmethod
    def set_inited(cls): # 实际可能传入Derived类
    cls.inited = True # 并没有修改Base.inited,而是给Derived添加了成员


    class Derived(Base):
    pass


    x = Derived()
    x.set_inited()
    if Base.inited:
    print("Base is inited") # 不会被执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    4.11 使用包(package)形式管理不同目录下的源码

    当多个Python源码文件分不同子目录存放时,用包(package)形式管理各个目录下的模块。

    说明: 通过让子目录包含__init__.py文件,可以让Python代码在import和from语句中,将子目录作为包名,通过分层来管理各个模块,让模块间的关系更清楚。__init__.py文件中可以包含这个包所需要的初始化动作,也可以定义一个__all__列表来指定from *语句会包含哪些模块。对于不需要初始化的包,可以只在目录下放一个名为__init__.py的空文件,标识这个目录是一个包。
    正确示例:
    假设Python源码根目录是dir0,其下有子目录dir1,dir1下面又有个子目录dir2,dir2下面有个mod.py模块。 那么,在dir1和dir2下各放置一个__init__.py文件,然后在其他代码中可以这样使用mod.py模块:

    import dir1.dir2.mod
    dir1.dir2.mod.func() # 调用mod.py中的func函数
    from dir1.dir2.mod import func # 把func函数添加到当前空间
    func() # 可以省掉包名和模块名直接调用
    1
    2
    3
    4
    4.12 避免在代码中修改sys.path列表

    说明: sys.path是Python解释器在执行import和from语句时参考的模块搜索路径,由当前目录、系统环境变量、库目录、.pth文件配置组合拼装而成。用户通过修改系统配置,可以指定搜索哪个路径下的模块。sys.path只应该根据用户的系统配置来生成,不应该在代码里面直接修改。否则可能出现A模块修改了sys.path,导致B模块搜索出错,且用户难以定位。
    正确示例: 如果要添加模块搜索路径,应当修改PYTHONPATH环境变量。如果是管理子目录,应当通过包(package)来组织模块。

    4.13 使用枚举替代range

    尽量不使用for i in range(x)的方式循环处理集合数据,而应使用for x in iterable的方式
    说明: for i in range(x),然后在循环体内对集合用下标[i]获取元素是C语言的编程习惯,它有很多缺点:容易越界;在循环体内修改i容易出错;可读性差。Python语言建议尽量用for x in iterable的方式直接取集合的每一条数据进行处理。

    # 错误示例:
    for i in range(len(my_list)):
    print(my_list[i])

    # 正确示例:
    for x in my_list:
    print(x)
    # 有些场合下,需要在处理时使用每个元素的序号,这时可以使用enumerate内置函数来给元素加上序号形成元组:
    my_list = ['a', 'b', 'c']
    for x in enumerate(my_list):
    print(x)
    # 运行结果为: (0, 'a') (1, 'b') (2, 'c')
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    4.14 避免重复定义变量名

    避免在无关的变量或无关的概念之间重用名字,避免因重名而导致的意外赋值和错误引用

    说明: Python的函数/类定义和C语言不同,函数/类定义语句实际上是给一个名字赋值。因此重复定义一个函数/类的名字不会导致错误,后定义的会覆盖前面的。但是重复定义很容易掩盖编码问题,让同一个名字的函数/类在不同的执行阶段具有不同的含义,不利于可读性,应予以禁止。 Python在解析一个被引用的名字时遵循LEGB顺序(Local - Enclosed - Global - Builtin),从内层一直查找到外层。内层定义的变量会覆盖外层的同名变量。在代码修改时,同名的变量容易导致错误的引用,也不利于代码可读性,应当尽量避免。
    ————————————————
    版权声明:本文为CSDN博主「zhao12501」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhao12501/article/details/115455806

    A thousand journey is started by taking the first step.
  • 相关阅读:
    Redis分布式锁的实现原理
    Redis锁的简单应用
    Redis所需内存 超过可用内存怎么办
    redis学习笔记之虚拟内存
    组织安全性SQL
    应收发票相关脚本
    用户与职责与请求关系语句
    应收事物处理删除 SQL 语句
    总账库存科目明细追溯
    月结各模块关闭情况查询
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/14995372.html
Copyright © 2020-2023  润新知