python实战打卡---day10

Python中的三⼤利器包括: 迭代器 , ⽣成器 , 装饰器 ,利⽤好它们才能开发出最⾼性能的Python程序,涉及到的内置模块 itertools提供迭代器相关的操作。此部分收录有意思的例⼦共计 15例。

  1. 寻找第n次出现位置

    def search_n(s, c, n):
        size = 0
        for i, x in enumerate(s):
            if x == c:
                size += 1
            if size == n:
                return i
        return -1
    print(search_n("fdasadfadf", "a", 3))# 结果为7,正确
    print(search_n("fdasadfadf", "a", 30))# 结果为-1,正确
    
  2. 斐波那契数列前n项

    def fibonacci(n):
        a, b = 1, 1
        for _ in range(n):
            yield a
            a, b = b, a + b
    list(fibonacci(5)) # [1, 1, 2, 3, 5]
    

    带有 yield 的函数在 Python 中被称之为 generator(生成器)。简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

    一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

    yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

    如何判断一个函数是否是一个特殊的 generator 函数?可以利用 isgeneratorfunction 判断:

    from inspect import isgeneratorfunction 
    isgeneratorfunction(fab) 
    
  3. 找出所有重复元素

    from collections import Counter
    def find_all_duplicates(lst):
        c = Counter(lst)
        return list(filter(lambda k: c[k] > 1, c))
    find_all_duplicates([1, 2, 2, 3, 3, 3]) # [2,3]
    
  4. 联合统计次数

    Counter对象间可以做数学运算:

    from collections import Counter
    a = ['apple', 'orange', 'computer', 'orange']
    b = ['computer', 'orange']
    ca = Counter(a)
    cb = Counter(b)
    #Counter对象间可以做数学运算
    ca + cb # Counter({'orange': 3, 'computer': 2, 'apple': 1})
    
    # 进⼀步抽象,实现多个列表内元素的个数统计
    def sumc(*c):
        if (len(c) < 1):
            return
        mapc = map(Counter, c)
        s = Counter([])
        for ic in mapc: # ic 是⼀个Counter对象
            s += ic
        return s
    #Counter({'orange': 3, 'computer': 3, 'apple': 1, 'abc': 1, 'face': 1})
    sumc(a, b, ['abc'], ['face', 'computer']) # Counter({'apple': 1, 'orange': 3, 'computer': 3, 'abc': 1, 'face': 1})
    
  5. groupby单字段分组

    # 天气记录
    a = [{'date': '2019-12-15', 'weather': 'cloud'},
    {'date': '2019-12-13', 'weather': 'sunny'},
    {'date': '2019-12-14', 'weather': 'cloud'}]
    # 按照天⽓字段 weather分组汇总:
    from itertools import groupby
    for k, items in groupby(a,key=lambda x:x['weather']):
        print(k)
    '''
    cloud
    sunny
    cloud
    '''
    

    输出结果看出,分组失败!原因:分组前必须按照分组字段 排序 ,这个很坑~

    a.sort(key=lambda x: x['weather'])
    for k, items in groupby(a,key=lambda x:x['weather']):
        print(k)
        for i in items:
            print(i)
    '''
    cloud
    {'date': '2019-12-15', 'weather': 'cloud'}
    {'date': '2019-12-14', 'weather': 'cloud'}
    sunny
    {'date': '2019-12-13', 'weather': 'sunny'}
    '''
    
  6. itemgetter和key函数

    注意到 sort和 groupby所⽤的 key函数,除了 lambda写法外,还有⼀种简写,就是使⽤ itemgetter:

    a = [{'date': '2019-12-15', 'weather': 'cloud'},
    {'date': '2019-12-13', 'weather': 'sunny'},
    {'date': '2019-12-14', 'weather': 'cloud'}]
    from operator import itemgetter
    from itertools import groupby
    a.sort(key=itemgetter('weather'))
    for k, items in groupby(a, key=itemgetter('weather')):
        print(k)
        for i in items:
            print(i)
    '''
    cloud
    {'date': '2019-12-15', 'weather': 'cloud'}
    {'date': '2019-12-14', 'weather': 'cloud'}
    sunny
    {'date': '2019-12-13', 'weather': 'sunny'}
    '''
    
  7. groupby多字段分组

    itemgetter是⼀个类, itemgetter(‘weather’)返回⼀个可调⽤的对象,它的参数可有多个:

    from operator import itemgetter
    from itertools import groupby
    a.sort(key=itemgetter('weather', 'date'))
    for k, items in groupby(a, key=itemgetter('weather')):
        print(k)
        for i in items:
            print(i)
    '''
    cloud
    {'date': '2019-12-14', 'weather': 'cloud'}
    {'date': '2019-12-15', 'weather': 'cloud'}
    sunny
    {'date': '2019-12-13', 'weather': 'sunny'}
    '''
    
  8. sum函数计算和聚合同时做

    Python中的聚合类函数 sum, min, max第⼀个参数是 iterable类型,⼀般使⽤⽅法如下:

    a = [4,2,5,1]
    sum([i+1 for i in a]) # 16
    

    使⽤列表⽣成式 [i+1 for i in a]创建⼀个长度与 a ⼀⾏的临时列表,这步完成后,再做 sum聚合。试想如果你的数组 a 长度⼗百万级,再创建⼀个这样的临时列表就很不划算,最好是⼀边算⼀边聚合, 稍改动为如下:

    a = [4,2,5,1]
    sum(i+1 for i in a) # 16
    

    此时 i+1 for i in a 是 (i+1 for i in a)的简写,得到⼀个⽣成器( generator)对象,如下所⽰:

    (i+1 for i in a) # <generator object <genexpr> at 0x0000019FC65A3F10>
    

    ⽣成器每迭代⼀步吐出( yield)⼀个元素并计算和聚合后,进⼊下⼀次迭代,直到终点。

  9. list分组(生成器版)

    from math import ceil
    def divide_iter(lst, n):
        if n <= 0:
            yield lst
            return
        i, div = 0, ceil(len(lst) / n)
        while i < n:
            yield lst[i * div: (i + 1) * div]
            i += 1
    list(divide_iter([1, 2, 3, 4, 5], 0)) # [[1, 2, 3, 4, 5]]
    list(divide_iter([1, 2, 3, 4, 5], 2)) # [[1, 2, 3], [4, 5]]
    
  10. 列表全展开(生成器版)

    #多层列表展开成单层列表
    a=[1,2,[3,4,[5,6],7],8,["python",6],9]
    def function(lst):
        for i in lst:
            if type(i)==list:
                yield from function(i)
            else:
                yield i
    print(list(function(a))) # [1, 2, 3, 4, 5, 6, 7, 8, 'python', 6, 9]
    
  11. 测试函数运行时间的装饰器

    #测试函数执⾏时间的装饰器⽰例
    import time
    def timing_func(fn):
        def wrapper():
            start=time.time()
            fn() #执⾏传⼊的fn参数
            stop=time.time()
            return (stop-start)
        return wrapper
    @timing_func
    def test_list_append():
        lst=[]
        for i in range(0,100000):
            lst.append(i)
    @timing_func
    def test_list_compre():
        [i for i in range(0,100000)] #列表⽣成式
    a=test_list_append()
    c=test_list_compre()
    print("test list append time:",a)
    print("test list comprehension time:",c)
    print("append/compre:",round(a/c,3))
    '''
    test list append time: 0.005001068115234375
    test list comprehension time: 0.0030002593994140625
    append/compre: 1.667
    '''
    
  12. 统计异常出现次数和时间的装饰器

    写⼀个装饰器,统计某个异常重复出现指定次数时,经历的时长。

    import time
    import math
    def excepter(f):
        i = 0
        t1 = time.time()
        def wrapper():
            try:
                f()
            except Exception as e:
                nonlocal i
                i += 1
                print(f'{e.args[0]}: {i}')
                t2 = time.time()
                if i == n:
                    print(f'spending time:{round(t2-t1,2)}')
        return wrapper
    

    关键词 nonlocal常⽤于函数嵌套中,声明变量i为⾮局部变量;

    如果不声明, i+=1表明 i 为函数 wrapper内的局部变量,因为在 i+=1引⽤(reference)时, i 未被声明,所以会报 unreferenced variable的错误。

    使⽤创建的装饰函数 excepter, n 是异常出现的次数。

    共测试了两类常见的异常: 被零除 和 数组越界 。

    n = 10 # except count
    @excepter
    def divide_zero_except():
        time.sleep(0.1)
        j = 1/(40-20*2)
    # test zero divived except
    for _ in range(n):
        divide_zero_except()
    @excepter
    def outof_range_except():
        a = [1,3,5]
        time.sleep(0.1)
        print(a[3])
    # test out of range except
    for _ in range(n):
        outof_range_except()
    '''
    division by zero: 1
    division by zero: 2
    division by zero: 3
    division by zero: 4
    division by zero: 5
    division by zero: 6
    division by zero: 7
    division by zero: 8
    division by zero: 9
    division by zero: 10
    spending time:1.12
    list index out of range: 1
    list index out of range: 2
    list index out of range: 3
    list index out of range: 4
    list index out of range: 5
    list index out of range: 6
    list index out of range: 7
    list index out of range: 8
    list index out of range: 9
    list index out of range: 10
    spending time:1.11
    '''
    
  13. 测试运行时长的装饰器

    #测试函数执⾏时间的装饰器⽰例
    import time
    def timing(fn):
        def wrapper():
            start=time.time()
            fn() #执⾏传⼊的fn参数
            stop=time.time()
            return (stop-start)
        return wrapper
    @timing
    def test_list_append():
        lst=[]
        for i in range(0,100000):
            lst.append(i)
    @timing
    def test_list_compre():
        [i for i in range(0,100000)] #列表⽣成式
    a=test_list_append()
    c=test_list_compre()
    print("test list append time:",a)
    print("test list comprehension time:",c)
    print("append/compre:",round(a/c,3))
    '''
    test list append time: 0.006001472473144531
    test list comprehension time: 0.0030012130737304688
    append/compre: 2.0
    '''
    
  14. 装饰器通俗理解

    再看⼀个装饰器:

    def call_print(f):
        def g():
            print('you\'re calling %s function'%(f.__name__,))
        return g
    

    使⽤ call_print装饰器:

    @call_print
    def myfun():
        pass
    @call_print
    def myfun2():
        pass
    

    myfun()后返回:

    myfun() # you're calling myfun function
    
    myfun2() # you're calling myfun2 function
    

    使⽤call_print

    你看, @call_print放置在任何⼀个新定义的函数上⾯,都会默认输出⼀⾏,你正在调⽤这个函数的 名。

    这是为什么呢?注意观察新定义的 call_print函数(加上@后便是装饰器):

    def call_print(f):
        def g():
            print('you\'re calling %s function'%(f.__name__,))
        return g
    

    它必须接受⼀个函数 f ,然后返回另外⼀个函数 g . 装饰器本质 本质上,它与下⾯的调⽤⽅式效果是等效的:

    def myfun():
        pass
    def myfun2():
        pass
    def call_print(f):
        def g():
            print('you\'re calling %s function'%(f.__name__,))
        return g
    

    下⾯是最重要的代码:

    myfun = call_print(myfun)
    myfun2 = call_print(myfun2)
    

    ⼤家看明⽩吗?也就是call_print(myfun)后不是返回⼀个函数吗,然后再赋值给myfun. 再次调⽤myfun, myfun2时,效果是这样的:

    myfun() # you're calling g function
    
    myfun2() # you're calling g function
    

    你看,这与装饰器的实现效果是⼀模⼀样的。装饰器的写法可能更加直观些,所以不⽤显⽰的这样赋 值: myfun = call_print(myfun), myfun2 = call_print(myfun2),但是装饰器的这种封装,猛 ⼀看,有些不好理解。

  15. 定制递减迭代器

    #编写⼀个迭代器,通过循环语句,实现对某个正整数的依次递减1,直到0.\
    from collections.abc import Iterator
    class Descend(Iterator):
        def __init__(self,N):
            self.N=N
            self.a=0
        def __iter__(self):
            return self
        def __next__(self):
            while self.a<self.N:
                self.N-=1
                return self.N
            raise StopIteration
    descend_iter=Descend(10)
    print(list(descend_iter)) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    

    核⼼要点: 1 _ _next _ _名字不能变,实现定制的迭代逻辑 2 raise StopIteration:通过 raise 中断程序,必须这样写

第四部分结束!!!!!!!!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leon.shadow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值