读书笔记《Effective Python 编写高质量Python代码的59个有效方法》(1~15)

1. 确定使用正确的Python版本(Python2/Python3)以及实现版本运行时环境(解释器)(CPython/Jython/IronPython/Pypy),建议使用Python3
    
2. 应严格按照PEP8的代码规范编写代码,使用同一套风格以便于协同开发项目以及后期维护工作

3. 确定不同情况下,Python3使用bytes(8位字节编码)、str(Unicode编码)类型,可编写辅助函数,如可接受str或者bytes并总是返回str的函数
    以及可接受str或者bytes并总是返回bytes的函数;一般情况下建议unicode编码使用utf-8的编码形式;
    返回str辅助函数示例代码:
        def to_str(bytes_or_str):
            if isinstance(bytes_or_str, bytes):
                value = bytes_or_str.decode(‘utf-8’)
            else:
                value = bytes_or_str
            return value
    网络或者文件一般以bytes传输数据,另外文件操作可根据open函数参数mode与encoding,以设置数据读取或者存储模式(二进制/字符串),
    当以二进制数据处理时,总应该以 'rb''wb'开启二进制模式

4. 用辅助函数来替代复杂的表达式
    对于比较复杂的表达式,可以考虑用辅助函数来替代复杂表达式,便于理解、此外若表达式使用比较频繁还可以可实现模块通用、共用性、

5. 熟练使用切片操作方法
    可方便地访问序列中的某些元素构成的子集,此外切片起止位置索引不会出现越界问题;
    注意:a=b与a=b[:]的区别,前者指向同一个序列集合,而后者是一个序列拷贝,操作各序列时互不影响;
    
6. 单次切片时,不要同时使用start、end、step
    单次切片若同时使用了三个值,则可能导致理解复杂、而且也容易出错;按照说法可以采用多步切片代替一次性切片的方式或者使用itertools模块替代;
    多步切片可能导致执行效率和内存占用的问题,而itertools则不会出现该类问题
    
7. 用列表推导来取代map和filter
    有时候使用列表推导可简化书写便于理解,采用列表推导中for、if等条件语句可实现map和filter结合的实现效果。此外map和filter可能需要借助于lambda表达式;
    此外还有其他如字典dict、集合set等也有推导机制,也可以使用该机制方便实现
    
8. 不要使用含有两个及其以上表达式的列表推导
    使用多个表达式的列表推导可能导致代码过长以及难以理解维护,可以使用多个条件、多个循环或者多个条件与循环;更甚者可以使用普通的if、for语言以及辅助函数;
    (由于列表推导支持多级循环(嵌套),每级循环支持多个条件,故此可能导致列表推导的复杂性)
    
9. 用生成器表达式来改写数据量较大的列表推导
    数据量比较大的时候用列表推导比较耗内存,可能导致程序崩溃,而生成器表达式返回的迭代器可以逐次取值,避免内存占用太多的问题;
    生成器表达式由实现放在一对圆括号中,类似于列表推导的写法,其只是将中括号改为圆括号即可,此后便可用nect迭代遍历。
    生成器表达式产生的结果迭代器也可以作为其他输入迭代器使用,形成连锁效应,而且执行速度也是很快的,不过其中的生成器用过之后不可再用,因迭代生成器时,其内部状态被改变
    (事实上生成器也可以认为是一种迭代器,但反过来却不成立,此外共同点便是均可迭代且迭代后的生成器或迭代器均会修改其内部状态)
    
10. 若需要获取对应列表或是可迭代对象的元素对应的下标索引,可用enumerate取代range
    获取可迭代对象的元素及其索引下标,可用len和range结合遍历各索引下元素,不过用enumerate更为方便简洁。
    此外enumerate还可以给定开始遍历位置,默认为0,并且遍历时返回以索引下标和对象元素的一个元组;

11. 用zip同时遍历两个迭代器
    在处理两个迭代器并且有一定的相互关联的时候,用zip可以把两个迭代器包装为生成器,以便后续求值,;
    在迭代zip对象时,也即是同时迭代两个迭代器并返回该元组。
    两个迭代器应保持相同的内容长度,避免产生一些意外结果或终止,或者控制好迭代长度也可。
    除了zip类外,itertools中也有zip_longest可用且不会出现长度不同而出现意外的问题;
    补充一点,zip或者zip_longest可以同时遍历两个以及以上的迭代器
    
12. 不要在for或者while循环后使用else块
    for或者while后面的else块表示若for执行完全,也即for中间没有break语句提前跳出或者for循环要遍历的序列为空,则会执行else语句块。
    同样的while循环条件为空,则也会执行else语句块
    for或者while循环后使用else块可以实现一些功能,不过表达不够清晰,容易误解,建议用辅助函数来实现更为直观,
    
13. 合理利用 try/except/else/finally 结构中的每个代码块
    try/except/else/finally在处理异常的过程中的不同时机,
    finally块,可以执行清理工作或者上传异常,进入try的语句块后,无论是否出现异常,此finally内的语句块一定会被执行;
    else语句块,若try未出现异常,则会执行else语句块,若使用else语句块则也必须要用到except语句块;
    此外else语句块还可以用来缩减 try 块中的代码量并把没有发生异常时所要执行的语句与try/except 代码块隔开;
    try/except/else/finally这4种语句块均用到,也是可以的,合理运用也可以表现的很好
    可能的组合:try,try/except,try/finally,try/except/else,try/except/else/finally;
    
14. 尽量用异常来表示特殊情况,而不要返回 None
    对于某些操作处理,返回异常时用None替代,可能导致一些问题(如用条件判断,0,None,空字符串均为False的)或者表示含义不够明确;
    建议直接抛出或者转换为可识别的异常类型再抛出,由调用者处理该异常情况,此外抛出的异常最好写到文档。
    
15. 了解如何在闭包里使用外围作用域中的变量
    闭包是一种定义在某个作用域中的函数,这种函数可以引用它所在的那个作用域里面的变量。
    当python解释器解析某个引用变量时,遍历作用域的顺序:
        1. 当前函数所在的作用域 
        2. 任何外围的作用域(包含该函数的其他函数)
        3. 包含当前代码的那个模块的作用域(全局作用域)
        4. 内置作用域(即包含len、str等函数所在的那个作用域)
    但是定义某个值或者说赋值的时候,若当前作用域存在该变量则赋新值,否则为新的变量且作用域便是当前所在的作用域,
    如果要修改上层的作用域的该同名变量值,则可使用python3中的nonlocal修饰该变量,再赋值;其类似于global的使用方法,不过nonlocal只能对上一层的
    修改,而不能像global可修改模块作用域的那个变量;此外nonlocal也应该只用到简短的函数内部,避免因nonlocal导致出现问题时难以查找的BUG。

 

posted @ 2019-10-10 20:57  浩月星空  阅读(226)  评论(0编辑  收藏  举报