python小叙-yield各种玩法

关于思考

前段时间刚用完scrapy刚抓完豆瓣和12306,发现yield在scrapy中被大量的引用,层层递进都是依靠yield作为返回。作为小白的我之前是不会玩yield这个高大上的东西,好吧,是我太菜了,主要是无法联想到什么应用场景😢。

1
2
3
4
def parse(self,response):
for href in response.css(".l_title a::attr(href)"):
full_url = response.urljoin(href.extract())
yield scrapy.Request(full_url,callback=self.parse_question)

在scrapy中yield被用来返回下一层的callback,不得不说scrapy在这里使用yield的方法真是nb,当爬虫的深度加大的时候,yield作用会凸显的很明显,想象一下

有一个n层的循环,如果你想要看最后一层n的运算结果,可以呀,那就是用n-1层for呗(当然我里不考虑合理的数据结构来降低程序的复杂度),

当运行到n-2层的时候,卧槽,出问题了,那怎么办!!调试完需要等n-3循环完。。什么鬼,数据量一大,简直了不知道什么时候可以看到结果

如果每一层都用yield来返回,n层有n个yield,那么只要一次你就可以看到n层的循环中的结果,哈哈,看了很屌,下面来个类似的demo,同样是循环,用yield实现出来是另一种感觉有木有

让我们来一个例子,求lis = [[“abcddsadasdasda”,”asdqwdqwdasdasda”],[“zxczxczx”,”vfdsvsdvsd”],[“asdascascsdcscscdsdc”],[“ascdsfsdfcascda”]]这个list种所有a的位置

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
55
56
57
58
encoding: utf-8
'''
Created Time: 三 7/27 08:23:01 2016
mail: liangyou.qiao@shuyun.com
Author: liangyou.qiao
File Name: yield_old.py
'''
def find_index(input_data):
def send_back(func):
def wrapper(*a,**kwd):
g = func(*a,**kwd)
g.send(None)
return g
return wrapper
@send_back
def sign(target):
while True:
words = yield
for w in words:
print("i am in callback_1 %s"%w)
target.send(w)
#target.send(None)
@send_back
def sort(target):
while True:
word = yield
for i in word:
print("i am in callback_2 %s"%i)
target.send(i)
@send_back
def squash(label):
while True:
word_list = yield
lis = list(word_list)
str_word = [ (i,label) for i in xrange(len(lis)) if lis[i] == label ]
print "i am callback 3",str_word
#for x in word_list:
# print("i am in callback_3 %s"%x)
print input_data
sign(sort(squash("a"))).send(input_data)
return
if __name__ == "__main__":
lis = [["abcddsadasdasda","asdqwdqwdasdasda"],["zxczxczx","vfdsvsdvsd"],["asdascascsdcscscdsdc"],["ascdsfsdfcascda"]]
result = find_index(lis)

使用yield是实现生产者消费者的多并发

使用yield就可以让函数变成一个生成器,那么根据这一点,就可以实现一种类似生产者消费者模型的方法,多个consumer执行同一个生成器的实例,用next()方法获取数据,终于在同事的大力帮忙之下,写出了这个demo,

只需要把循环体转化成生成器,在不使用queue的情况下,实现了p/c的模型

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# -*- coding: utf-8 -*-
'''
File Name: muti.py
Author: WillDX
mail: xiang.dai@shuyun.com
Created Time: 2016年07月19日 星期二 11时02分21秒
i'''
import sys
import time
from threading import *
class Consumer(Thread):
"""继承Thread方法,并重写__init__()方法和run()方法"""
__produce = None
__func = None
__results = []
def __init__(self, group=None, target=None, name=None,args=(), kwargs=None, verbose=None):
super(Consumer, self).__init__(group, target, name, args, kwargs, verbose)
self.__args = args
def run(self):
try:
while True:
params = Consumer.__produce.next()
#print "params:",params,type(params)
obj = Consumer()
res = obj.__func(**params)
Consumer.__results.extend(res)
#print "[线程名]:%s;[参数]:%s;[返回值]:%s"%(currentThread().name,params,res)
except ValueError as e:
print e
except StopIteration as e:
#print "finished:%s" % currentThread().name
print e
@classmethod
def add_produce(cls,produce):
cls.__produce=produce
return cls()
@classmethod
def set_func(cls,func):
cls.__func = staticmethod(func)
@classmethod
def get_result(cls):
return cls.__results
def func(i):
return [i + 10000]
def main():
produce = ( {"i":i} for i in range(30))
#执行
mp = ( Consumer() for i in range(10))
Consumer.add_produce(produce)
Consumer.set_func(func)
tsk = []
for i in mp:
i.start()
tsk.append(i)
for t in tsk:
t.join()
res = Consumer.get_result()
print res,len(res)
if __name__ == "__main__":
main()

关于问题

1.    多个线程同时操作yield,yield是否是线程安全的?
2.    在使用send给yield发送数据的时候,为什么第一次要传送None