用过scrapy的同学都知道,scrapy中会出现yield这个关键字。开始的时候我其实我知道他是生成器的一种标志,但并不能深刻的理解这个东西。在工作中的一些场景中我深刻的体会到了生成器的好用之处。
首先我举一些应用场景来帮助大家理解为何我们要使用这个生成器。假如现在我们需要生成1到100的数字。我们很容易想到用列表推导式。
numbers = [i for i in range(1,101)]
print(numbers)
试想下如果我们要生成的数值很多时,当我们还用列表推导式或者循环生成的话,是不是这些数值占用了很大的内存呢?有没有一种方式我们定义好要生成的规则,当我们需要的时候我们在生成呢?
numbers = (i for i in range(1,101))
print(numbers)
print(next(numbers))
for i in numbers:
print(i)
numbers是一个生成器,定义了我们生成100个数的规则,next()函数执行取出数值。生成器是一个可迭代的对象。通过循环可以取出数值。for 循环就相当于next()函数的功能。
用yield可以这么写:
def get_num():
for i in range(100):
yield i
a = get_num()
for i in a:
print(i)
在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
再举个例子,比如现在有个需求需要查询数据库表中的数据。但是数据很多,需要设计翻页查询。但是如果数据非常多,该怎么设计才合理呢?不可能一直循环然后查完,再拿来用吧。很理想的是查一点用一点,边查边用。这边这样设计,假设每次查100条数据。通过定义生成器的规则来实现
def get_data():
limit=100, offset=0
while True:
query_sql = '''
'''
conn = pymysql.connect(host=MYSQL_HOST, port=MYSQL_PORT, user=MYSQL_USER_NAME, passwd=MYSQL_PASSWORD,
db="", charset='UTF8')
cursor = conn.cursor()
try:
cursor.execute(query_sql % (limit, offset))
result = cursor.fetchall()
if not result:
break
offset += limit
# result双重tuple转成list
result_list = []
for tuple_single in result:
result_list.append(tuple_single[0])
logging.info(f"")
yield result_list
except Exception as ex:
logging.info(f"查询失败,原因: {ex}")
finally:
cursor.close() # 关闭游标
conn.close() # 释放数据库资源
get_data就是定义的一个生成器。这样我们就能循环这个生成器来获取数据了。