zoukankan      html  css  js  c++  java
  • python通用规范-8

    文章目录

    8.1 可变参数默认值设为`None`
    8.2 对子类继承的变量要做显式定义和赋初值
    8.3 严禁使用注释行等形式仅使功能失效
    8.4 慎用`copy`和 `deepcopy`
    8.5 系统路径推荐使用 `pathlib.Path`
    8.6 使用`subprocess`模块代替`os.system`模块来执行`shell`命令
    8.7 建议使用with语句操作文件
    8.1 可变参数默认值设为None

    函数参数中的可变参数不要使用默认值,在定义时使用None
    说明:参数的默认值会在方法定义被执行时就已经设定了,这就意味着默认值只会被设定一次,当函数定义后,每次被调用时都会有"预计算"的过程。当参数的默认值是一个可变的对象时,就显得尤为重要,例如参数值是一个list或dict,如果方法体修改这个值(例如往list里追加数据),那么这个修改就会影响到下一次调用这个方法,这显然不是一种好的方式。应对种情况的方式是将参数的默认值设定为None。
    错误示例:

    >>> def foo(bar=[]): # bar is optional and defaults to [] if not specified
    ... bar.append("baz") # but this line could be problematic, as we'll see...
    ... return bar
    1
    2
    3
    在上面这段代码里,一旦重复调用foo()函数(没有指定一个bar参数),那么将一直返回’bar’。因为没有指定参数,那么foo()每次被调用的时候,都会赋予[]。下面来看看,这样做的结果:

    >>> foo()
    ["baz"]
    >>> foo()
    ["baz", "baz"]
    >>> foo()
    ["baz", "baz", "baz"]

    # 正确示例:None是不错的选择
    >>> def foo(bar=None):
    ... if bar is None: # or if not bar:
    ... bar = []
    ... bar.append("baz")
    ... return bar
    ...
    >>> foo()
    ["baz"]
    >>> foo()
    ["baz"]
    >>> foo()
    ["baz"]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    8.2 对子类继承的变量要做显式定义和赋初值

    说明:在Python中,类变量都是作为字典进行内部处理的,并且遵循方法解析顺序(MRO)。子类没有定义的属性会引用基类的属性值,如果基类的属性值发生变化,对应的子类引用的基类的属性的值也相应发生了变化。
    # 错误示例:
    class A(object):
    x = 1
    class B(A):
    pass
    class C(A):
    pass
    >>> B.x = 2
    >>> print(A.x, B.x, C.x)
    1 2 1
    >>> A.x = 3
    >>> print(A.x, B.x, C.x)
    3 2 3
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    这里虽然没有给C.x赋值,但是由于基类的值A.x发生改变,在获取C.x的值得时候发现它引用的数据发生了变化。在上面这段代码中,因为属性x没有在类C中发现,它会查找它的基类(在上面例子中只有A,尽管Python支持多继承)。换句话说,就是C自己没有x属性,因此,引用C.x其实就是引用A.x。

    # 正确示例:
    # 如果希望类`C`中的`x`不引用自`A`类,可以在`C`类中重新定义属性`X`,
    # 这样`C`类的就不会引用`A`类的属性`x`了,值的变化就不会相互影响。
    class B(A):
    x = 2
    class C(A):
    x = -1
    >>> print(A.x, B.x, C.x)
    1 2 -1
    >>> A.x = 3
    >>> print(A.x, B.x, C.x)
    3 2 -1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    8.3 严禁使用注释行等形式仅使功能失效

    说明:python的注释包含:单行注释、多行注释、代码间注释、doc string等。除了doc string是使用""""""括起来的多行注释,常用来描述类或者函数的用法、功能、参数、返回等信息外,其余形式注释都是使用#符号开头用来注释掉#后面的内容。基于python语言运行时编译的特殊性,如果在提供代码的时候提供的是py文件,即便是某些函数和方法在代码中进行了注释,别有用心的人依然可以通过修改注释来使某些功能启用;尤其是某些接口函数,如果不在代码中进行彻底删除,很可能在不知情的情况下就被启用了某些本应被屏蔽的功能。因此根据红线要求,在python中不使用的功能、模块、函数、变量等一定要在代码中彻底删除,不给安全留下隐患。即便是不提供源码py文件,提供编译过的pyc、pyo文件,别有用心的人可以通过反编译来获取源代码,可能会造成不可预测的结果。
    # 错误示例:在 main.py 中有两个接口被注释掉了,但是没有被删除。
    if __name__ == "__main__":
    if sys.argv[1].startswith('--'):
    option = sys.argv[1][2:]
    if option == "load":
    #安装应用
    LoadCmd(option, sys.argv[2:3][0])
    elif option == 'unload':
    #卸载应用
    UnloadCmd(sys.argv[2:3][0])
    elif option == 'unloadproc':
    #卸载流程
    UnloadProcessCmd(sys.argv[2:3][0])
    # elif option == 'active':
    # ActiveCmd(sys.argv[2:3][0])
    # elif option == 'inactive':
    # InActiveCmd(sys.argv[2:3][0])
    else:
    Loginfo("Command %s is unknown"%(sys.argv[1]))
    # 在上例中很容易让其他人看到我们程序中的两个屏蔽的接口,容易造成不安全的因素,注释的代码应该删除。

    # 正确实例
    if __name__ == "__main__":
    if sys.argv[1].startswith('--'):
    option = sys.argv[1][2:]
    if option == "load":
    #安装应用
    LoadCmd(option, sys.argv[2:3][0])
    elif option == 'unload':
    #卸载应用
    UnloadCmd(sys.argv[2:3][0])
    elif option == 'unloadproc':
    #卸载流程
    UnloadProcessCmd(sys.argv[2:3][0])
    else:
    Loginfo("Command %s is unknown"%(sys.argv[1]))
    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
    30
    31
    32
    33
    34
    35
    36
    8.4 慎用copy和 deepcopy

    说明:在python中,对象赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用。如果需要拷贝对象,需要使用标准库中的copy模块。copy模块提供copy和deepcopy两个方法:
    copy浅拷贝:拷贝一个对象,但是对象的属性还是引用原来的。对于可变类型,比如列表和字典,只是复制其引用。基于引用所作的改变会影响到被引用对象。
    deepcopy深拷贝:创建一个新的容器对象,包含原有对象元素(引用)全新拷贝的引用。外围和内部元素都拷贝对象本身,而不是引用。
    Notes:对于数字,字符串和其他原子类型对象等,没有被拷贝的说法。如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已。使用copy和deepcopy时,需要了解其使用场景,避免错误使用。

    # 示例:
    >>> import copy
    >>> a = [1, 2, ['x', 'y']]
    >>> b = a
    >>> c = copy.copy(a)
    >>> d = copy.deepcopy(a)
    >>> a.append(3)
    >>> a[2].append('z')
    >>> a.append(['x', 'y'])
    >>> print(a)
    [1, 2, ['x', 'y', 'z'], 3, ['x', 'y']]
    >>> print(b)
    [1, 2, ['x', 'y', 'z'], 3, ['x', 'y']]
    >>> print(c)
    [1, 2, ['x', 'y', 'z']]
    >>> print(d)
    [1, 2, ['x', 'y']]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    8.5 系统路径推荐使用 pathlib.Path

    使用pathlib.Path库中的方法代替字符串拼接来完成文件系统路径的操作

    说明:pathlib.Path 基于 os.path库实现了一系列文件系统路径操作方法,这些方法相比单纯的路径字符串拼接来说更为安全,而且为用户屏蔽了不同操作系统之间的差异。
    # 错误示例:如下路径字符串的拼接在Windows操作系统无法使用
    path = os.getcwd() + '/myDirectory'

    # 正确示例:
    path = pathlib.Path(os.getcwd(), 'myDirectory')
    path = pathlib.Path().resolve() / 'myDirectory'
    1
    2
    3
    4
    5
    6
    8.6 使用subprocess模块代替os.system模块来执行shell命令

    说明:subprocess模块可以生成新进程,连接到它们的input/output/error管道,并获取它们的返回代码。该模块旨在替换os.system等旧模块,相比os.system模块来说更为灵活。
    # 推荐做法:
    >>> subprocess.run(["ls", "-l"]) # doesn't capture output
    CompletedProcess(args=['ls', '-l'], returncode=0)
    >>> subprocess.run("exit 1", shell=True, check=True)
    Traceback (most recent call last):
    ...
    subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
    >>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
    CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
    stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null ', stderr=b'')
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    8.7 建议使用with语句操作文件

    说明:Python 对一些内建对象进行改进,加入了对上下文管理器的支持,可以用于 with 语句中。使用 with 语句可以自动关闭文件,减少文件读取操作错误的可能性,在代码量和健壮性上更优。注意 with 语句要求其操作的类型实现 __enter__() 和 __exit__() 方法,需确认实现后再使用。
    # 推荐做法:
    with open(r'somefileName') as somefile:
    for line in somefile:
    print(line)
    # ...more code
    # 此使用with语句的代码等同于以下使用try...finally...结构的代码。

    somefile = open(r'somefileName')
    try:
    for line in somefile:
    print(line)
    # ...more code
    finally:
    somefile.close()
    ————————————————
    版权声明:本文为CSDN博主「zhao12501」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhao12501/article/details/115473209

    A thousand journey is started by taking the first step.
  • 相关阅读:
    深入分析String类型(一)
    创建泛型类
    XML数据的读取—数据库配置文件
    Config配置文件读写
    jQuery动画
    设置屏幕快捷键
    jQuery事件
    jQuery操作DOM
    Python 二进制文件网址
    Centos使用crontab自动定时备份mysql的脚本
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/14995386.html
Copyright © 2011-2022 走看看