zoukankan      html  css  js  c++  java
  • python中的那些“神器”

    “武林至尊,宝刀屠龙,号令天下,莫敢不从,倚天不出,谁与争锋”,这是神器。不过今天要说的python中的“神器”就没有这么厉害了,这里要说的“神器”其实就是名称里面带了个“器”的,如下:

    1. 列表解析器
    2. 迭代器
    3. 生成器
    4. 装饰器

    列表解析器

    现在遇到了这样一个问题需要解决:“有一个数字的列表,要求对该列表中的奇数乘以2,返回处理完成后的列表(不改变原来列表的顺序,仅对列表中的奇数乘以2)”,比较传统的方法可能会是这样的:

    def double_odd_number_list(odd_number_list):
        ret_value = []
        for number in odd_number_list:
            if number % 2:
                ret_value.append(number * 2)
            else:
                ret_value.append(number)
        return ret_value
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    测试数据:

    if __name__ == '__main__':
        test_odd_number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 24, 13, 28]
        print double_odd_number_list(test_odd_number_list)
    • 1
    • 2
    • 3

    测试结果: 
    这里写图片描述 
    这样做对于新手来说比较容易理解,但代码不够精简,我们来看看列表解析器怎样完成上面的工作:

    print [(number*2) if number % 2 else number
               for number in test_odd_number_list]
    • 1
    • 2

    代码说明:

    1. for number in test_odd_number_list迭代test_odd_number_list,对于test_odd_number_list中的每一个元素,如果为奇数(if分支)则往结果列表中放入number*2,否则放入number(else分支)

    test_odd_number_list还是上面定义的,测试结果如下: 
    这里写图片描述
    上面讲的可能还满足不了你全部的需求,比如下面的情况: 
    这里写图片描述

    生成器表达式

    列表解析器在生成的列表数据量很大时会占用很大的内存,它会一次性生成全部的数据保存到列表中并返回,无论是计算时间上还是内存消耗上,当数据量达到一定量级时都是无法接受的。一个改进方法就是使用生成器表达式,生成器表达式在语法上与列表解析唯一的区别就是讲[]换成了()。它会返回一个生成器,一次只计算一个值(延迟计算),如下图: 
    这里写图片描述

    迭代器

    迭代器的详细内容可以参照我的另外一篇博客《python的迭代器

    生成器

    谈到生成器,不得不谈到python的一个关键字yield,python中关于生成器的定义是:“A function which returns an iterator”,该函数与普通还是除了包含yield关键字外没有任何区别。yield关键字的作用是挂起函数的执行,下次调用时所有环境恢复到挂起时的状态从挂起的地方继续执行,一般都是放到循环里。我们来看一个求阶乘的例子: 
    这里写图片描述 
    有几点需要作出说明:

    1. 一个生成器函数中可以有多个yield函数,像上例
    2. 函数执行的顺序跟普通函数一致,可以理解成yield就是一个return,但return后该函数就挂起在该点,下次调用该函数时继续从该点开始执行。例如factorial(2),函数会直接执行到while循环(因为n!=0),第一次循环到yield时ret *= i,计算的结果是1,返回1并挂起,此时i=1,ret等于1,再一次调用时从yield的下一行执行,i变成2,小于等于传入的n=2,继续循环(没有从函数的头部执行),再到yield时ret=2,返回2并挂起,依次类推
    3. 通常比较少用,我第一次在代码中看到是ceilometer(openstack一个子项目)中

    装饰器

    装饰器,装点门面用,此处的门面就是函数,用来装饰函数或者方法,在python中我们习惯性的把绑定到实例上的叫方法,未绑定的叫函数,也就是说在类里面第一个参数为self的就是方法,在模块中的就是函数。python中的装饰器有两种:带参数的装饰器,不带参数的装饰器

    不带参数的装饰器

    我们先从语法上相对简单的不带参数的装饰器讲起。我们在评估性能时,可能会精确到某个具体的函数执行一次需要花费的时间,新手最新想到的应该是写一个脚本,调用前和调用后分别获取一下当前时间,两者之差就是该函数执行所消耗的时间,代码可能是这样的:

    def test(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    if __name__ == '__main__':
        start = datetime.datetime.utcnow()
        test((1, 2, 3, 4, 5, 6), 3)
        end = datetime.datetime.utcnow()
        print (end - start).total_seconds()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码容易理解,但如果有很多个函数都需要做这种测试呢?是不是就需要重复写很多次的:

    start = datetime.datetime.utcnow()
    end = datetime.datetime.utcnow()
    print (end - start).total_seconds()
    • 1
    • 2
    • 3

    如果你想成为一个优秀的程序员,那么,尽你所能去避免做重复的事情,让你写的每一行代码发挥最大的价值,下面来看一个使用装饰器的例子:

    import datetime
    import time
    
    __author__ = 'Administrator'
    
    
    def time_counter(func):
        def doc_func(*args, **kwargs):
            start = datetime.datetime.utcnow()
            ret = func(*args, **kwargs)
            end = datetime.datetime.utcnow()
            return ret, start, end, (end - start).total_seconds()
        return doc_func
    
    
    @time_counter
    def test(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    if __name__ == '__main__':
        print test((1, 2, 3, 4, 5, 6), 3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    time_counter是一个装饰器,它跟普通函数的区别在于:

    1. 参数为函数对象(time_counter的参数func)
    2. 在函数里面定义了一个新的函数(time_counter中的doc_func),且该函数(time_counter中的doc_func)的参数列表与第1步中的函数对象(time_counter的参数func)的参数列表一致
    3. 函数最后返回了新定义的函数对象(time_counter中的doc_func)
    4. @time_counter def func(*args, **kwargs):等价于func = time_counter(func) 
      在任何需要计算执行时间的函数上使用该装饰器即可,如下:
    @time_counter
    def test(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    • 1
    • 2
    • 3
    • 4

    当执行test函数时,会先将test函数对象作为参数传递到time_counter装饰器,也就是形式参数func,然后依次执行定义的doc_func中的全部操作,其中调用func的地方(ret = func(*args, **kwargs))会调用test函数,下面是上述代码的执行结果: 
    这里写图片描述

    带参数的装饰器

    上面的装饰器,在使用时不需要传递任何参数,但有时这还不足以满足我们的需求,假设我们在一套测试框架中,测试用例可能有多种级别,完全测试时我们需要执行所有等级的测试用例,如果某次发布仅仅是修改了其中的一小块功能,对于未修改的部分,我们仅需要测试基本功能即可,例如级别大于等于2的。我们来看一下使用带参数的装饰器模拟实现该功能:

    import datetime
    import time
    
    __author__ = 'Administrator'
    
    
    def skip_unit_test(func):
        print 'skip function:%s' % func.func_name
    
    
    def unit_test_filter(level):
        def new_decorator(func):
            def final_func(*args, **kwargs):
                if level >= 2:
                    start = datetime.datetime.utcnow()
                    ret = func(*args, **kwargs)
                    end = datetime.datetime.utcnow()
                    print ret, start, end, (end - start).total_seconds()
                else:
                    skip_unit_test(func)
            return final_func
        return new_decorator
    
    
    @unit_test_filter(0)
    def test_level_zero(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    
    @unit_test_filter(1)
    def test_level_one(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    
    @unit_test_filter(2)
    def test_level_two(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    
    @unit_test_filter(3)
    def test_level_three(ret_value, sleep_time):
        time.sleep(sleep_time)
        return ret_value
    
    
    if __name__ == '__main__':
        test_level_zero((1, 2, 3, 4, 5, 6), 3)
        test_level_one((1, 2, 3, 4, 5, 6), 3)
        test_level_two((1, 2, 3, 4, 5, 6), 3)
        test_level_three((1, 2, 3, 4, 5, 6), 3)
    
    • 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

    看起来比较复杂,我们来总结一下带参数装饰器的特性:

      1. 是一个函数,有一个或者多个参数,返回一个定义的子函数对象
      2. 函数体中定义一个子函数,子函数的参数是函数对象,同“不带参数装饰器”中的func,子函数仍然返回定义的孙子函数对象
      3. 子函数中再定义一个孙子函数,孙子函数的参数列表与子函数参数func的参数列表一致,并在其中做一些处理,且最终会调用到被装饰的函数(单次装饰的函数可能不会被调用)
      4. 在使用时需要按照装饰器函数的参数列表来传递参数,如例子中的:@unit_test_filter(0)等 
        下面附上上述例子的执行结果: 
        这里写图片描述
  • 相关阅读:
    PHP概率算法---砸金蛋示例
    onethink----网站配置信息调用!
    ajax解决跨域方法(适用于自己写接口解决跨域)
    JavaScript获取地址栏的参数!
    Atitit. null错误的设计 使用Optional来处理null
    Atitit.常用的gc算法
    Atitit.java expression fsm 表达式分词fsm引擎
    Atitit.安全性方案规划设计4gm  v1 q928
    Atitit.安全性方案规划设计4gm  v1 q928
    Atitit.java expression fsm 表达式分词fsm引擎
  • 原文地址:https://www.cnblogs.com/whwywzhj/p/6266803.html
Copyright © 2011-2022 走看看