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.
  • 相关阅读:
    ionic localstorage
    angular 中文鏈接
    把jqmobi 變成jQuery 的插件 從此使用jQuery
    jqmobi 的一些設置
    ionic ngcordova map 地圖
    ionic pull to refresh 下拉更新頁面
    json 對象的序列化
    鍵盤彈出,頁面佈局被推上去了.....
    Cordova V3.0.0中config.xml配置文件的iOS Configuration
    android ios 只能輸入數字 不能輸入小數點的 函數 cordova
  • 原文地址:https://www.cnblogs.com/chengjian-physique/p/14995377.html
Copyright © 2011-2022 走看看