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

    文章目录

    5.1 异常处理
    5.1.1 异常捕获后要加 `finally`
    5.1.2 异常捕获时需注明异常类型
    5.1.3 不在`except`分支里面的`raise`都必须带异常
    5.1.4 尽量用异常来表示特殊情况,而不要返回None
    5.1.5不在`finally`中使用`return`或者`break`
    5.1.6 禁止使用`except X, x`语法
    5.2 异常恢复
    5.3 断言
    5.1 异常处理

    5.1.1 异常捕获后要加 finally

    使用try…except…结构对代码作保护时,需要在异常后使用finally…结构保证操作对象的释放
    说明:
    使用try…except…结构对代码作保护时,如果代码执行出现了异常,为了能够可靠地关闭操作对象,需要使用finally…结构确保释放操作对象。
    示例:

    handle = open(r"/tmp/sample_data.txt") # May raise IOError
    try:
    data = handle.read() # May raise UnicodeDecodeError
    except UnicodeDecodeError as decode_error:
    print(decode_error)
    finally:
    handle.close() # Always run after try:
    1
    2
    3
    4
    5
    6
    7
    5.1.2 异常捕获时需注明异常类型

    不要使用except:语句来捕获所有异常

    说明:
    在异常这方面, Python非常宽容,except:语句真的会捕获包括Python语法错误在内的任何错误。使用except:很容易隐藏真正的bug,我们在使用try…except…结构对代码作保护时,应该明确期望处理的异常。 Exception类是大多数运行时异常的基类,一般也应当避免在except语句中使用。通常,try只应当包含必须要在当前位置处理异常
    的语句,except只捕获必须处理的异常。比如对于打开文件的代码,try应当只包含open语句,except只捕获FileNotFoundError异常。对于其他预料外的异常,则让上层函数捕获,或者透传到程序外部来充分暴露问题。
    # 错误示例:
    # 如下代码可能抛出两种异常,使用"except:"语句进行统一处理时,如果是open执行异常,将在"except:"语句之后handle无效的情况下调用close,报错handle未定义。
    try:
    handle = open(r"/tmp/sample_data.txt") # May raise IOError
    data = handle.read() # May raise UnicodeDecodeError
    except:
    handle.close()

    # 正确示例:
    try:
    handle = open(r"/tmp/sample_data.txt") # May raise IOError
    try:
    data = handle.read() # May raise UnicodeDecodeError
    except UnicodeDecodeError as decode_error:
    print(decode_error)
    finally:
    handle.close()
    except (FileNotFoundError, IOError) as file_open_except:
    print(file_open_except)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    5.1.3 不在except分支里面的raise都必须带异常

    说明:raise关键字单独使用只能出现在try-except语句中,重新抛出except抓住的异常。

    # 错误示例:
    >>> a = 1
    >>> if a==1:
    ... raise
    ...
    Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
    TypeError: exceptions must be old-style classes or derived from BaseException, not
    NoneType


    # 正确示例1:raise一个Exception或自定义的Exception
    >>> a = 1
    >>> if a==1:
    ... raise Exception
    ...
    Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
    Exception

    # 正确示例2:在try-except语句中使用
    >>> import sys
    >>>
    >>> try:
    ... f = open('myfile.txt')
    ... s = f.readline()
    ... i = int(s.strip())
    ... except IOError as e:
    ... print("I/O error({0}): {1}".format(e.errno, e.strerror))
    ... except ValueError:
    ... print("Could not convert data to an integer.")
    ... except:
    ... print("Unexpected error:", sys.exc_info()[0])
    ... raise

    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
    5.1.4 尽量用异常来表示特殊情况,而不要返回None

    当我们在一个工具方法时,通常会返回None来表明特殊的意义,比如一个数除以另外一个数,如果被除数为零,那么就返回None来表明是没有结果的

    def divide(a, b):
    try:
    return a/b
    except ZeroDivisionError:
    return None
    result = divide(x, y)
    if result is None:
    print('Invalid inputs')
    1
    2
    3
    4
    5
    6
    7
    8
    当分子为零是会返回什么?应该是零(如果分母不为零的话),上面的代码在if条件检查就会被忽略掉,if条件不仅仅只检查值为None,还要添加所有条件为False的情况了

    x, y = 0, 5
    result = divide(x, y)
    if not result:
    print('Invalid inputs') #This is wrong!
    1
    2
    3
    4
    上面的情况是python编码过程中很常见,这也为什么方法的返回值为None是一种不可取的方式,这里有两种方法来避免上面的错误。

    1.第一种方法是将返回值分割成一个tuple,第一部分表示操作是否成功,第二部分是实际的返回值(有点象go语言里的处理)

    def divide(a, b):
    try:
    return True, a / b
    except ZeroDivisionError:
    return False, None
    1
    2
    3
    4
    5
    调用此方法时获取返回值并解开,检查第一部分来代替之前仅仅检查结果。

    success, result = divide(x, y)
    if not success:
    print('Invalid inputs')
    1
    2
    3
    这种方式会带来另外一个问题,方法的调用者很容易忽略掉tuple的第一部分(通过在python里可以使用_来标识不使用的变量),这样的代码乍一看起来不错,但是实际上和直接返回None没什么两样。

    _, result = divide(x, y)
    if not result:
    print('Invalid inputs')
    1
    2
    3
    2.接下来,另外一种方式,也是推荐的一种方式,就是触发异常来让调用者来处理,方法将触发ValueError来包装现有的ZeroDivisionError错误用来告诉方法调用者输入的参数是有误的。

    def divide(a, b):
    try:
    return a / b
    except ZeroDivisionError as e:
    raise ValueError('Invalid inputs') from e
    1
    2
    3
    4
    5
    那么方法调用者必须要处理错误输入值而产生的异常(方法的注释应该注明异常情况)。同时也不用去检查返回值,因为当方法没有异常抛出时,返回值一定是对的,对于异常的处理也是很清晰。

    x, y = 5, 2
    try:
    result = divide(x, y)
    except ValueError:
    print('Invalid inputs')
    else:
    print('Result is %.1f' % result)
    >>>
    Result is 2.5
    1
    2
    3
    4
    5
    6
    7
    8
    9
    需要记住的:
    (1)方法使用None作为特殊含义做为返回值是非常糟糕的编码方式,因为None和其它的返回值必须要添加额外的检查代码。
    (2)触发异常来标示特殊情况,调用者会在捕获异常来处理。

    5.1.5不在finally中使用return或者break

    避免finally中可能发生的陷阱,不要在finally中使用return或者break语句
    通常使用finally语句,表明要释放一些资源,这时候try和except还有else代码块都被执行过了,如果在执行它们的过程中有异常触发,且没有处理这个异常,那么异常会被暂存,当finally代码执行后,异常会重新触发,但是当finally代码块里有return或break语句时,这个暂存的异常就会丢弃:

    def f():
    try:
    1/0
    finally:
    return 42
    print(f())
    1
    2
    3
    4
    5
    6
    上面的代码执行完后1/0产生的异常就会被忽略,最终输出42,因此在finally里出现return是不可取的。
    当try块中return,break,continue执行时,finally块依然会被执行。

    def foo():
    try:
    return 'try'
    finally:
    return 'finally'
    >>> foo()
    'finally'
    1
    2
    3
    4
    5
    6
    7
    最终方法的输出其实不是正确的结果,但出现这个问题的原因是错误使用了return和break语句。

    5.1.6 禁止使用except X, x语法

    禁止使用except X, x语法,应当使用except X as x

    说明: except X, x语法只在2.X版本支持,3.X版本不支持,有兼容性问题。而且,except X, x写法容易和多异常捕获的元组(tuple)表达式混淆。因此应该统一用except X as x方式。
    5.2 异常恢复

    方法发生异常时要恢复到之前的对象状态
    说明:当发生异常的时候,对象一般需要——如果是关键的安全对象则必须——维持其状态的一致性。常用的可用来维持对象状态一致性的手段包括:
    1.输入校验(如校验方法的调用参数)
    2.调整逻辑顺序,使可能发生异常的代码在对象被修改之前执行
    3.当业务操作失败时,进行回滚
    4.对一个临时的副本对象进行所需的操作,直到成功完成这些操作后,才把更新提交到原始的对象
    5.避免需要去改变对象状态

    # 错误示例:
    PADDING = 2
    MAX_DIMENSION = 10
    class Dimensions:
    def __init__(self, length, width, height):
    self.length = length
    self.width = width
    self.height = height
    def get_volume_package(self, weight):
    self.length += PADDING
    self.width += PADDING
    self.height += PADDING
    try:
    self.validate(weight)
    volume = self.length * self.width * self.height
    self.length -= PADDING
    self.width -= PADDING
    self.height -= PADDING
    return volume
    except Exception , ex:
    return -1
    def validate(self, weight) :
    # do some validation and may throw a exception
    if weight>20:
    raise Exception
    pass
    if __name__ == '__main__'
    d = Dimensions(10, 10, 10)
    print(d.getVolumePackage(21)) # Prints -1 (error)
    print(d.getVolumePackage(19)) # Prints 2744 instead of 1728
    # 在这个错误示例中,未有异常发生时,代码逻辑会恢复对象的原始状态。但是如果出现异常事件,则回滚代码不会被执行,从而导致后续的getVolumePackage()调用不会返回正确的结果。

    # 正确示例1:回滚
    except Exception as ex:
    self.length -= PADDING
    self.width -= PADDING
    self.height -= PADDING
    return -1
    # 这个正确示例在getVolumePackage()方法的except块中加入了发生异常时恢复对象状态的代码。

    # 正确示例2:finally子句
    def getVolumePackage(self, weight):
    self.length += PADDING
    self.width += PADDING
    self.height += PADDING
    try:
    self.validate(weight)
    volume = self.length * self.width * self.height
    return volume
    except Exception as ex:
    return -1
    finally:
    self.length -= PADDING
    self.width -= PADDING
    self.height -= PADDING
    # 这个正确示例使用一个finally子句来执行回滚操作,以保证不管是否发生异常,都会进行回滚。

    # 正确示例3:输入校验
    def getVolumePackage(self, weight):
    try:
    self.validate(weight)
    except Exception as ex:
    return -1
    self.length += PADDING
    self.width += PADDING
    self.height += PADDING
    volume = self.length * self.width * self.height
    self.length -= PADDING
    self.width -= PADDING
    self.height -= PADDING
    return volume
    # 这个正确示例在修改对象状态之前执行输入校验。注意,try代码块中只包含可能会抛出异常的代码,而其他代码都被移到try块之外。

    # 正确示例4:未修改的对象
    def getVolumePackage(self, weight):
    try:
    self.validate(weight)
    except Exception as ex:
    return -1
    self.length += PADDING
    self.width += PADDING
    self.height += PADDING
    volume = (self.length + PADDING) * (self.width + PADDING) * (self.height + PADDING)
    return volume
    # 这个正确示例避免了需要修改对象,使得对象状态不可能不一致,也因此没有必要进行回滚操作。相比之前的解决方案,更推荐使用这种方式。但是对于一些复杂的代码,这种方式可能无法实行。

    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    5.3 断言

    assert语句通常只在测试代码中使用,禁止在生产版本中包含assert功能
    assert语句用来声明某个条件是真的。例如,如果你非常确信某个列表中至少有一个元素,而你想检验这一点,并且在它非真时触发一个异常,那么assert就是这种场景下的不二之选。当assert语句失败的时候,会触发AssertionError异常

    >>> mylist = ['item']
    >>> assert len(mylist) >= 1
    >>> mylist.pop()
    'item'
    >>> assert len(mylist) >= 1
    Traceback (most recent call last): File "<stdin>", line 1, in ? AssertionError
    1
    2
    3
    4
    5
    6
    assert只应在研发过程中内部测试时使用,出现了AssertionError异常说明存在软件设计或者编码上的错误,应当修改软件予以解决。在对外发布的生产版本中禁止包含assert功能。
    ————————————————
    版权声明:本文为CSDN博主「zhao12501」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhao12501/article/details/115456774

    A thousand journey is started by taking the first step.
  • 相关阅读:
    不用再去找rem了,你想要的rem都在这
    linux下ftp配置文件详解
    Linux chmod命令修改文件与文件夹权限命令代码
    如何在linux下开启FTP服务
    解决ftp客户端连接验证报错Server sent passive reply with unroutable address. Using server address instead
    预定义编译器宏
    类的成员变量修饰 const 和static
    【转】svn http://提示svn: Unrecognized URL scheme错误
    EVEREST Ultimate Edition 5.50 正式版 序列号
    [转]Linux下查看文件和文件夹大小
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/14995377.html
Copyright © 2011-2022 走看看