一、生成器

生成器,就是自己用代码写的迭代器,生成器的本质就迭代器。

之前的,将一个可迭代对象 转化 成为迭代器。

li = [1,2,3,5,18]    # 这是一个列表 可迭代对象

iter1 = li.__iter__()   # 将一个迭代对象 转换成 迭代器 

iter1 = iter(li)    # 这个是内置函数,本质上还是调用对象的__iter__()方   

 

       x = 15
iter(x) # TypeError: 'int' object is not iterable
上面的li 是python源码提供的基本数据类型之一 列表,而 __iter__() 或者 iter()都是源码提供的,不是我们自己写的

 

用以下两种方式构建一个生成器:

  1,通过生成器函数

  2,生成器表达式  

生成器函数:

# 生成器函数
# 首先来看一看,普通函数的定义
def func1(x):
    x += 1
    print(x)
    return x
result = func1(10)   # 对于普通函数,函数名()就是调用或执行函数,将会执行函数体的代码
print(result)
# 生成器函数
def generator_func1(x):
    x += 1
    print(333)
    yield x
    x += 3
    print(444)
    yield x
g_obj = generator_func1(10)  # 调用或执行生成器函数,并不会执行函数体,而是在内存中创建一个生成器函数对象
# print(g_obj)  # <generator object generator_func1 at 0x00000145FDCD0E60>

# 问题来了?那么如何 执行 生成器函数对象 对应的 函数的函数体呢? 答案,调用生成器函数对象的__netx__()方法,即生成器的本质就是迭代器
v1 = g_obj.__next__()  # 11 当第一次调用生成器对象的__next__()方法时,从对应的生成器函数开头,从上往下 向下执行,当遇到第一个yield,返回yield后面的
                      # 值 ,函数挂起。
print(v1)   # 11

v2 = g_obj.__next__()  # 14 # 从函数上次执行到的yield语句 下 的第一条语句,继续向下执行,下到遇到yield语句,返回yield后面的值
print(v2)

v3 = g_obj.__next__()  #   v3 = g_obj.__next__() StopIteration   当取值都取完了,再取就报错,停止迭代

# 总结 : 一个__next__() 或者 next(g_obj) 方法  对应一个 yield 
# yield 将其后面的值,如果是一个数据,原封不动返回,如果多个值 ,放在元组里返回,将值返回给  生成器对象.__next__()方法

# 生成器函数 yield  与 普通函数  return 的区别
# return 结束函数,给函数的执行者返回值
# yield 不会结束函数,一个next 对应 一个 yield , 给生成器对象.__next__() 返回值 。

生成器函数 vs 迭代器:

# 生成器函数  VS 迭代器
# 区别1: 自定制的区别
l1 = [1,2,3,4,5]  # 比较死板,不灵活
iter1 = iter(l1)

def func1(x):
    x += 1
    yield x
    x += 3
    yield x
    x += 5
    yield x
g1 = func1(5)   # 国为生成器,是通过生成器函数 创建的, 函数可以传参,就显得比较灵活
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())

# 区别2 : 内存级别的区别
# 迭代器需要通过可迭代对象进行转化。 可迭代对象非常占用内存
# 生成器直接创建,不需要转化,从本质上就节省内存。

def func1():
    for i in range(1,100000):
        yield i
g2 = func1()
for i in range(50):
    print(g2.__next__())

count = 1
while count < 51:
    print(g2.__next__())
    count += 1


# send 与  __next__()区别:

def func10():
    print(111)
    count = yield 6
    print(222)
    print(count)
    count1 = yield 7
    print(count1)
    yield 8
g10 = func10()
print(g10.__next__())  # 111 6
print(g10.send("send1"))   # 222 send1 7
print(g10.send("send2"))   # send2 8

# send 与 next()方法,或者 生成器对象.__next__()方法一样,也是对生成器取值的方法。取值就是执行一个yield
# send 可以上一个yield 整个表达式传值 ,如果yield 左边有变量接受,则赋值给它。
# 第一次取值 永远都是next
# 最后一个yield 永远也得不到send传的值。

def  cloth(n):
    for i in range(1,n+1):
        yield "第%d件衣服成功产出" % i
g_cloth = cloth(1000)   # 创建了一个可以产生1000件衣服的生成器,但是是一件一件地产出的

# print(g_cloth.__next__())
# print(g_cloth.__next__())

# 下指令,生产50件衣服
# for i in range(50):
#     print(g_cloth.__next__())
def  cloth2(n):
    for i in range(1,n+1):
        yield "第%d件衣服成功产出" % i,"共消耗成本{}元".format(i * 85)
g_cloth2 = cloth2(10000)
count = 1
while count < 51:
    print("".join(list(next(g_cloth2))))
    # print(next(g_cloth2))
    count += 1

 

 

def cloth2(n):
for i in range(n+1):
yield "衣服 %s" % i
generator2 = cloth2(1000)
for num in range(51): # range(5) ,也是一个迭代对象,数字范围太大,也占用内存间
print(generator2.__next__()) # 调用生成器函数对象的__next__() 或者 send() 方法,将推动 对应的生成器函数执行,
# 执行到,遇到下一个yield 语句,函数挂起,下次调用__next__() 或者 send() 方法,上一个yield
# 语句之后,继续 执行,直到下一个yield语句,依次类推。
count = 1
while count < 51: # 继续生产50件
print(next(generator2))
count += 1


def fuc_gene():
x = 5
print(x)
yield x,10
x = {'name':"chris",'age':18}
print(x)
yield x,[1,2,3] # 返回时,多个值放到一个元组中,和return一样
gene = fuc_gene() # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象
print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。
print(gene.__next__())

# 列表推导模式:一行代码几乎搞定你需要的任何的列表
# 循环模式 [变量(加工后的变量) for 变量 in iterable ]
l = [i for i in range(1,101)]
print(l)

l2 = ["开学第%d几天" % i for i in range(1,101)]
print(l2)
l3 = [i*i for i in range(1,11)]
print(l3)

# 筛选模式 [ 变量 (加工后的变量) for 变量 in iterable if 条件 ]
l4 = [i for i in range(1,31) if i % 2 == 0]
print(l4)
l5 = [i**2 for i in range(1,31) if i % 3 == 0]
print(l5)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]

name = [name for list1 in names for name in list1 if name.count("e") == 2]
print(names)
print([j for i in names for j in i if j.count("e") == 2])
print(name)

# 列表推导式
# 优点:一行解决,方便。
# 缺点:容易着迷,不是易排错,不能过超过三次循环
# 列表推导式不能解决所有列表的问题,所以不要太刻意用。

# 生成器表达式:将列表推导式的 [] 转换成 () 即可
g = (i for i in range(1,11)) # 得到一个生成器
print(g) # <generator object <genexpr> at 0x000001D0C61922B0>
# print(g.__next__())
# while True:
# try:
# print(next(g))
# except StopIteration:
# break
# li_test = [print(5) for i in range(5)] # 打印 5 次 5
# print(li_test) # [None, None, None, None, None]

#
test2 = [print(10) for i in range(5)] # 遍历 for 循环中的 可迭代对象,每取一个值 ,执行for循环前面的语句,将值 追加到列表当中,
# print()没有返回值 ,所以是None, ...注意:列表推导式,for 前面只能一条语句,多条的话,就
# 要考虑用函数来封装,且执行列表推导式时,函数执行
def tell(n):
print("追加第%d个元素到当前列表中" % n)
return n

test3 = [tell(i) for i in range(1,6)]
'''
输出结果:
追加第1个元素到当前列表中
追加第2个元素到当前列表中
追加第3个元素到当前列表中
追加第4个元素到当前列表中
追加第5个元素到当前列表中
'''
print(test3) # [1, 2, 3, 4, 5]

# 生成器表达式扩展

sum2 = 0
def sum1(n): # 刚才犯一个错误,将全局变量和函数名,取名一样,我们说函数名就是特殊的变量,不能重名,否则执行时会出现意想不到的错误
global sum2
sum2 += n
return sum2
# print(sum2)


g1 = (sum1(i) for i in range(1,11)) # 列表生成器表式式,会将普通函数的 return 语句 转换成 yield 语句
print(g1)
print(sum2)
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())

print("-*"*30)
# 我的猜想,然后,再根据 for 循环的次数变成生成器函数:
sum2 = 0
def g1_func(): #
start = 1
while start < 11: # 这样,就节省内存空间,不需要事先创建可迭代对象
global sum2
sum2 += start
start += 1
yield sum2
g2 = g1_func()
print(g2.__next__())
print(g2.__next__())
print(g2.__next__())


# 字典推导式:
mcase = {'a':10,'b':34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)
mcase.get('a')

 

# 其它推导式:
# 合并大小写对应的value值,将k统一成小写
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
# mcase_frequency = {k.lower(): mcase.get(k.lower(),0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
# print(mcase_frequency)

# dict.get(key, default=None)
print(mcase.get('b',1))
print(reversed([1,3,5]))


# 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
li2 = [(x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1]
print(li2)
li2_iter = ((x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1)
print(li2_iter) #<generator object <genexpr> at 0x000001F4D2FDC200>


for i in li2_iter: # 迭代器,也可以for 循环
print(i)

 

 

 

 

# def g_function():
#     print(555)
#     ret1 = yield {1,3}
#     print(666)
# g = g_function()
# print(g.__next__())
# print(g.__next__())    # 一个 next 对应一个 yield , 生成器函数 ,只有一个yield 所以报错


def g_function():
    print(555)
    ret1 = yield {1,3}
    print(ret1)
    print(666)
    yield "success"
g = g_function()
print(g.__next__())
# print(g.__next__())  # None 666 success
print(g.send("hello world"))   # 上一个yield 表达式,传值 ,赋值 给 左边的变量    # hello world 666 success

 

 

 

生成器函数 与 普通函数的区别:

def fuc_gene():
    x = 5
    print(x)
    yield x,10
    x = {'name':"chris",'age':18}
    print(x)
    yield x,[1,2,3]   # 返回时,多个值放到一个元组中,和return一样
gene = fuc_gene()    # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象
print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。
print(gene.__next__())

 

列表推导式一些例子:

有一个列表 :l1 = ['alex', 'WuSir', '老男孩', '太白']将其构造成这种列表['alex0', 'WuSir1', '老男孩2', '太白3']
l1 = ['alex','WuSir','老男孩','大白3']
dest4 = [e+str(index) for index,e in enumerate(l1)]
print(dest4)
posted on 2018-08-20 13:24  yellwonfin  阅读(163)  评论(0编辑  收藏  举报