【Python3_进阶系列_010】Python3-生成器

一、生成器

 在Python中,使用生成器可以很方便的支持迭代器协议。生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们状态,来自动实现迭代协议。

下面通过几个实例来了解生成器的工作:

例子1:

def fun(n):
    i=0
    while i<n:
        print("开始",i)
        yield i
        i+=1
        print("结束",i)
b=fun(5)
next(b)
next(b)

输出:
开始 0

结束 1
开始 1

执行过程:第一个next(b),执行到print(“开始”,0),遇到yeild就停止。第二个next(b),会从上一个yield之后开始执行,一直到下一个yield。也就是输出结束1,开始1.

特点1:函数里面加入yield之后,函数就是生成器

也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。
生成器函数返回生成器的迭代器。 “生成器的迭代器”这个术语通常被称作”生成器”。
要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是__next__()。如同迭代器一样,我们可以使用__next__()函数来获取下一个值。

在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

实例2:生成器实现斐波那契数列

先看一下传统的方法:
def fab(maxs):
    n,a,b = 0,0,1
    while n < maxs:
        print(b)
        a,b = b,a+b
        n = n + 1

fib = fab(6)

一次性输出:
1
2
3
5
8
加入生成器:
def fab(maxs):
    n,a,b = 0,0,1
    while n < maxs:
        yield b ##yield只能依次读取,不能从中间读取
        a,b = b,a+b
        n = n + 1

fib = fab(6)
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))
print(next(fib))

依次输出:
1
1
2
3
5

可以看到生成器实现的时候,我们可以按照需要进行取值,而不用一次性输出全部的数据。这个功能在读文件的时候,如果文件很大,我们可以用生成器去读,就不会占很大的内存空间

二、面试题

1.小数位步长实现:range函数的步长都是》=1的,我们实现一个函数,支持小数位的步长。例如 rangex(0,1,0.1).依次返回0,0.1,0.2,0.3.。。。

def range_powerfull(start,end,step):
    if isinstance(start,(int,float)) and isinstance(end,(int,float)):
        while start <end:
            start
            yield float(start)
            start+=step
    else:
        print('输入错误,开始和结束位置请输入整数或者浮点数')

a = range_powerfull(0,1,0.1)
b = range_powerfull(3,10,0.75)
c = range_powerfull('c',5,0.23)

for x in a:
    print(x)

for y in b:
    print(y)

输出:
0.0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999


3.0
3.75
4.5
5.25
6.0
6.75
7.5
8.25
9.0
9.75

可以通过for循环一次全部取出值,也可以通过next()函数一个一个的取值。

2.

实现装饰器测试函数的运行时间,现在要求,在这个装饰器的基础上,完善这个装饰器。使其:
1. 依然可以测试函数运行的时间
2. 依然可以将函数运行的时间输出到屏幕上
3. 除了以上两点,还可以将运行时间,以日志的形式记录在指定的文件中(当作日志)。
4.测试函数大家自己选取

 

import time

def run_time(func):
    def new_fun(*args):
        t0 = time.time()
        back = func(*args)
        times = time.time() - t0
        print('run time: %s' % (times))
        with open('log.txt', 'a+') as f:
            f.write('---%s\t' % (str(time.asctime(time.localtime()))))
            f.write('run time: %f\n' % (times))
        return back, times

    return new_fun


@run_time
def bu(x):
    for i in range(len(x)):
        for j in range(i,len(x)):
            if x[i]>=x[j]:
                x[i],x[j]=x[j],x[i]
    return x

print(bu([2,3,6,1,3,2,7]))

输出:
([1, 2, 2, 3, 3, 6, 7], 0.0)
并且追加写入log

3.在编辑文本中,我们经常需要在文本中找到某个模式出现的位置。比如我要在文本中找到某个字符串出现的次数和位置,这种问题称为字符串匹配问题。我们简化为在一个字符串中找到模式出现的位置。

如:字符串为:str1='adhvahabcabcabcfjiaohgio'
模式为:str2='abc'
找出str1中模式出现的位置(6,9,12)

str1='adhvahabcabcabcfjiaohgio'
str2='abc'

str11='das3212345mn*1232123)'
str22='32123'

def find_index(pattern,original):
    li = []
    for i in range(len(original)):
        if original[i]==pattern[0] and original[i:i+len(pattern)]==pattern:
            li.append(i)
    return li

print(find_index(str2,str1))
print(find_index(str22,str11))
输出:

[6, 9, 12]
[3, 15]

4.字符串里面的count可以统计字符出现的次数,现在要求大家定义这样的一个类,这个类里有一个方法可以统计字符串里的所有的字符出现的次数,并作为一个字典返回,同时这个类还有字符串的所有方法。

import os, hashlib

class new_str(str): ##继承str就可以有字符串的全部方法
    def __init__(self, str1):
        self.str1 = str1

    def count(self):
        dic = {}
        for i in set(self):
            dic[i] = int(super().count(i))
        return dic


randstr = hashlib.sha1(os.urandom(32)).hexdigest()

a = new_str(randstr)
print(a.count())

输出:{'a': 1, '3': 3, '4': 2, '9': 3, '6': 2, 'f': 2, '7': 6, 'd': 3, '8': 2, 'c': 5, '1': 2, '0': 4, '5': 2, '2': 3}

 

posted @ 2018-08-02 15:40  爱寂寞撒的谎言  阅读(163)  评论(0)    收藏  举报