zoukankan      html  css  js  c++  java
  • python | 改善Python程序建议- Part2

    这个系列主要是总结一些《改善python程序的91个建议》的学习笔记,希望可以对自己和读者有所帮助。本文是该系列第二部分,第一部分请见 Part1

    21 多使用else 让程序变的更加pythonic
    eg: try-except-else-finally
     
    22 异常处理注意点
    · 注意异常粒度,不推荐在try中放入过多的代码 最好保持异常粒度的一致性和合理性
    · 异常捕获尽量具体指定,少用Except 使用单独的except 最好能够使用raise将异常抛出
    · 注意异常捕获的顺序 (…)
    · 使用更为友好的异常信息,遵守异常参数的规范

    23 避免finally中可能发生的陷阱

    finally中产生新的异常或者执行了return or break 那么临时报错的异常就会丢失,导致异常屏蔽

    24 深入理解None,正确判断对象是否为空

    python 以下数据会当做空来处理:
    · 常量None 常量False
    · 任何形式的数值类型0 eg:0,0L ,0.0,0j
    · 空的序列字典,如”(),[],{}
    · 当用户定义的类中定义了nonzero()和len()方法,并且该方法返回bool值false或者整数0
       常量None的特殊性体现在它 既不是0orFalse,它就是一个空值对象,数据类型为NoneType,遵循单例模式
       None与任何其他非None的对象比较结果为False None !={}
       一般判断空列表 直接 if list or if len(list)

    25 连接字符串应优先使用join而不是+ —-> 字符串规模比较大的时候

    26 python 格式化字符串尽量使用 str.format 而不是%
    eg: 
    ‘xxxx{0}xxxx{1}’.format(n1,n2)
    最直接的理由:% 最终会被.format 方式代替
     
    27 区别对待可变对象和不可变对象
    · 不可变对象: 数字,字符串,元组
    · 可变对象 : 字典,列表,字节数
    · 注意:列表的切片操作相当于浅拷贝 —>list1=[1,2,3] list2=list1[:] id(list1)!=id(list2)
     
    28 使用列表解析 like [x for x in range(10)]
    · 代码更简洁
    · 效率更高 但是大数据不是最佳的选择,过多的内存消耗可能会导致memoryError
     
    29 警惕默认参数潜在的问题
    def xxx(xx,a=[]) 不要使用列表等可变类型 def实际是可执行语句 默认参数也会被计算
    ===>应修改为 def xxx(xx,a=None)
     
    30 慎用变长参数
    · 使用过于灵活,可能破坏程序的健壮性
    · 如果一个函数的参数列表很长,虽然可以通过使用*args 和 **kwargs 来简化函数的定义
       但通常这意味着这个函数可以有更好的实现方式,应该被重构,用一个序列来保存需要传递的参数
    适合场景是: 装饰器 读取配置文件
     
    31 深入理解str()和repr()的区别
    · str()主要面向用户,目的是可读性,返回可读性强的字符串类型
       repr()面向的是Python解释器or 开发人员,返回值表示python解释器内部的含义
    · 在解释器中直接输入a时默认调用repr()函数,而print a 则 调用的是str()
    · 一般来说类中毒定义了__repr__()方法,而__str__()则为可选,没有__str__(),则默认会使用__repr__()
     
    32 分清staticmethod和classmethod的适用场景
    · 静态方法:实现功能不和实例相关也不和类相关的独立方法,如一些字符验证等,定义在类中能有效将代码组织起来,从而使相关代码的垂直距离更近,提高代码的可维护性;当然,如果有一组独立的方法,将其定义在一个模块中,通过模块来访问这些方法也是不错的选择
    · 类方法: 减少子类的代码重写, 降低代码的冗余
     
    33 字符串的基本用法
    · 多行字符串使用下面的方式
    s = (
        'aaaaa'
        'bbbb'
        'ccccc'
    )

    · 3对双引号会把换行符和前导空格当做字符串的一部分

    · str.startswith和endswith中匹配参数可以使用元组,有一个匹配上就返回T

    · 判断字符串包含子串的判断推荐使用 in 和 not in
    ·  ''.split() ==> [] ''.split(' ') ==> ['']
     
    34 按需选择sort()或者sorted()
    · sorted()会保留原有列表生成新的列表
    · sorted功能非常强大,而sort只能排一般的列表
     
    35 使用Counter 进行计数统计

    from collections import Counter
    some_data = ['a','2',2,4,5,'2','b',4,7,'a',5,'d','a','z']
    print Counter(some_data)
    >>>Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})

    · Counter类属于字典类的子类,是一个容器对象,主要用来统计散列对象。

    # 取值
    xx = Counter(some_data)
    print xx.['a']

    36 单例模式

    保证系统中一个类只有一个实例而且该实例易于被外界访问,从而方便对实例个数的控制并节约系统资源
    class Test(object):
        is_instance = None
        is_first = True
    
        def __new__(cls, *args, **kwargs):
            if cls.is_instance == None:
                cls.is_instance = object.__new__(cls)
    
            return cls.is_instance
    
        def __init__(self, name):
            if Test.is_first:
                self.name = name
                Test.is_first = False
    
    
    test1 = Test('lf1')
    
    print(id(test1))       # 2136967041656
    print(test1.name)      # lf1
    
    test2 = Test('lf2')     
    
    print(id(test2))        # 2136967041656
    print(test2.name)       # lf1

    37 利用cProfile 定位性能瓶颈

    程序运行慢的原因有很多,但真正的原因往往是一两段设计并不那么良好的不起眼的程序。程序性能影响往往符合8/2法则,即20%的代码运行时间占用了80%的总运行时间(实际上,比例要夸张得多,通常是几十行代码占用了95%以上的运行时间)。
    profile是python的标准库,可以统计程序里每一函数的运行时间,并提供了多样的报表,cprofile则是它的C实现版本,剖析过程本身需要消耗的资源更少。
    ## test_cprofile.py
    import time
    def foo():
        sum = 0
        for i in range(100):
            sum += i
            time.sleep(.5)
        return sum
    if __name__ == "__main__":
        foo()
    现在使用profile分析这个程序
    if __name__ =="__main__":
        import cProfile
        cProfile.run("foo()")

    输出如下:

    第二种方式:python -m cProfile test_cprofile.py

    · cProfile 的统计结果以及各项意义

    统计项意义
    ncalls 函数的调用次数
    tottime 函数总计运行时间,不含调用的函数运行时间
    percall 函数运行一次的平均时间,等于tottime/ncalls
    cumtime 函数总计运行时间,含调用的函数运行时间
    percall 函数运行一次的平均时间,等于cumtime/ncalls
    filename:lineno(function) 函数所在的文件名,函数的行号,函数名
      将cProfile的输出保存到文件,并以各种形式来查看结果.
      1)使用cProfile.run()函数再提供一个实参,就是保存输出的文件名;同样,在命令行参数里,多一个参数,用来保存cProfile的输出
      2)通过pstats模块的另一个类Stats来解决.
    # ....略
    if __name__ == "__main__":
        import cProfile
        cProfile.run("foo()", "prof.txt")
        import pstats
        p = pstats.Stats("prof.txt")
        # sort_stats(key,[...]) 以key排序  print_stats()输出结果
        p.sort_stats("time").print_stats()
      sort_stats的key

    参数参数的意义
    ncalls 被调用次数
    cumulative 函数运行的总时间
    file 文件名
    module 模块名
    pcalls 简单调用统计(兼容旧版,未统计递归调用)
    line 行号
    name 函数名
    nfl Name,file,line
    stdname 标准函数名
    time 函数内部运行时间(不计调用子函数的时间)

    38 掌握循环优化的基本技巧

    · 减少循环内部的计算

    # 1
    for i in range(iter):
        d = math.sqrt(y)
        j += i*d
    
    # 2   比第一种快 40%~60%
    d = math.sqrt(y)
    for i in range(iter):
        j += i*d

    · 将显式循环改为隐式循环

    """求等差数列1,2,3,...n的和"""
    # 1 
    sum = 0
    n = 10
    for i in range(n+1):
        sum = sum + i
    
    print(sum)
    
    # 2   直接用数学知识 n*(n+1)/2  负面影响就是牺牲了代码的可读性 需添加清晰和恰当的注释是非常必要的
    n = 10 
    print(n*(n+1)/2)

    · 在循环中尽量引用局部变量.在python命名空间中局部变量优先搜索,因此局部变量的查询会比全局变量要快

    # 1
    x = [10,34,56,78]
    def f(x):
        for i in range(len(x)):
            x[i] = math.sin(x[i])
        return x
    
    # 2  性能提高10%~15%
    def g(x):
        loc_sin = math.sin
        for i in range(len(x)):
            x[i] = loc_sin(x[i])
        return x

    · 关注内层嵌套循环,尽量将内层循环的计算往上层移,减少内层的计算

    # 1
    for i in range(len(v1)):
        for j in range(len(v2)):
            x = v1p[i]+ v2[j]
    
    # 2
    for i in range(len(v1)):
        vli = v1[i]
        for j in range(len(v2)):
            x = vli + v2[j]

    参考:

    《改善python程序的91个建议》

    https://blog.csdn.net/qq_31603575/article/details/80177153

    https://www.cnblogs.com/cotyb/p/5452602.html

    https://www.cnblogs.com/cotyb/tag/pythonic/

    https://medium.freecodecamp.org/an-a-z-of-useful-python-tricks-b467524ee747

  • 相关阅读:
    @FeignClient同一个name使用多个配置类报错,解决方案【转+改进】
    spring cloud 与spring boot 版本不匹配引发的问题总结
    无聊系列
    基于redis+lua实现的分布式限流
    elasticsearch 优化笔记
    redis+lua库存扣减和冲正
    es-dsl笔记
    wiki—-Confluence搭建
    Linux常用命令
    SQLSERVER|CDC 日志变更捕获机制
  • 原文地址:https://www.cnblogs.com/geo-will/p/9711828.html
Copyright © 2011-2022 走看看