一.python生成器简介
在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 生成器的主要作用:
- 可是当我们的数据特别大的时候建立一个列表的储存数据就会很占内存的。这时生成器就派上用场了。它可以说是一个不怎么占计算机资源的一种方法。
- 生成器可以有效的减小一些数据处理过程的内存使用;
- 生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值;
- 因此生成器看起来像是一个函数,但是表现得却像是迭代器
二.python生成器案列
2.1 生成器表达式
L = [x*x for x in range(10)]
print(L)
g = (x*x for x in range(10))
print(g)
output:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x7f91487794c0>
要创建一个generator,最简单的方法就是,只有把一个列表生成式的[]中括号改为()小括号,就创建一个generator。 由于generator保存的是算法,每次调用next(generaotr_ex)就计算出他的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误,而且上面这样不断调用是一个不好的习惯,正确的方法是使用for循环,因为generator也是可迭代对象,生成器在本质上是可迭代的。
g = (x*x for x in range(10))
print(g)
for n in g:
print(n)
<generator object <genexpr> at 0x7f9148475e08>
0
1
4
9
16
25
36
49
64
81
2.2生成器函数
def fib(max):
n,a,b = 0,0,1
while n < max:
yield b
a,b = b,a+b
n =n+1
f = fib(7)
print(f)
for n in f:
print(n)
output:
<generator object fib at 0x7f9148475f68>
1
1
2
3
5
8
13
def triangle():
_list,new_list = [],[]
while True:
if len(_list) == 0:
new_list.append(1)
else:
for i in range(len(_list)+1):
if i ==0:
new_list.append(1)
elif i == len(_list):
new_list.append(1)
else:
new_list.append(_list[i-1]+_list[i])
yield new_list
_list = new_list.copy()
new_list = []
def triangle_print():
n = 0
for t in triangle():
n = n + 1
print(t)
if n == 12:
break
triangle_print()
def triangles():
m = [1]
while True:
yield m
m = [1] +[m[x]+m[x+1] for x in range(len(m)-1)] +[1]
def triangles_print():
n = 0
for t in triangles():
n = n + 1
print(t)
if n == 11:
break
triangle_print()
output:
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
三.python生成器的具体应用
3.1 python生成器在文本数据预处理中的应用
对于一些文本字符数据,我们需要对其进行分词,正则清洗,分割等一系列文本预处理操作。我们利用函数生成器来获取预处理结果将会很大程度上减小项目内存。
import re
def load_stopwords():
"""
加载停用词
:return:
"""
sw = set()
with open("train/stop_word.txt", 'r') as f:
for line in f.readlines():
sw.add(line[:-1])
return sw
def word_lemmatize(context):
stopword = ['at','based','in','of','for','on','and','to','an','using','with','the','by','we','be','is','are','can','or','no','from','like']
stopword1 = load_stopwords()
context = re.sub('[^a-zA-Z]', ' ', context).strip().lower().split()
for word in context:
if word not in stopword and word not in stopword1 and len(word)> 2:
yield word
if __name__ == '__main__':
pstr ='Trypanosoma cruzi DNA replication includes the sequential recruitment of pre-replication and replication machineries close to nuclear periphery'
print(' '.join(word_lemmatize(pstr)).strip().lower().split())
output:
['trypanosoma', 'cruzi', 'dna', 'replication', 'includes', 'sequential', 'recruitment', 'pre', 'replication', 'replication', 'machineries', 'close', 'nuclear', 'periphery']
3.2 NLP词向量训练
在使用一些包训练词向量的过程中往往需要加载很大的语料,如果服务器内存比较小的话,往往加载大一些的语料都没法实现,更不用说训练语料了。因此需要利用生成器一点一点读取语料。 当数据集特别大的时候,直接加载大语料就需要消耗特别多的内存,gensim等词向量训练工具包支持使用迭代生成器的方式输入。
比如,我们利用生成器逐个读取lmdb数据库中的文本数据,gensim迭代生成器来训练。
'''利用生成器读取语料,并训练的一般写法'''
class MySentences(object):
def __init__(self, dirname):
self.dirname = dirname
def __iter__(self):
for fname in os.listdir(self.dirname):
for line in open(os.path.join(self.dirname, fname)):
yield line.split()
sentences = MySentences('/some/directory')
model = Word2Vec(sentences)
'''生成器从lmdb加载语料,并且迭代训练'''
class MySenteces1():
def __init__(self, dirname):
self.LMDB_NAME = dirname
self.lc = LMDBClient(self.LMDB_NAME)
def __iter__(self):
with self.lc.db.begin() as txn:
for k in txn.cursor():
author_feature = data_utils.deserialize_embedding(k[1])
random.shuffle(author_feature)
yield author_feature
class Word2vecModel:
def __init__(self, name="sci_alldata"):
self.model = None
self.name = name
def train(self, wf_name, size=EMB_DIM):
data = MySenteces1('sci_all_data_feature')
self.model = Word2Vec(data,size=100,negative =5, min_count=2, window=5,workers=16)
self.model.save(join(settings.EMB_DATA_DIR, '{}.emb'.format(wf_name)))
3.3 生成器用于将字典分割成多个字典
对于一个比较大的字典数据集,我们需要按照一定的补偿分割成多个字典进行分块处理。比如多进程处理数据,就需要把一个数据集分成多个,每个进程处理一部分。
import json
import multiprocessing
import pandas as pd
from os.path import join
def load_json(rfdir, rfname):
with codecs.open(join(rfdir, rfname), 'r', encoding='utf-8') as rf:
return json.load(rf)
def chunks(data, SIZE=1000):
for i in range(0, len(data), SIZE):
yield dict(list(data.items())[i:i+SIZE])
if __name__ =='__main__':
name_pubs = load_json('data', 'sci_author_data_50_all.json')
print('同名论文集数量为:',len(name_pubs))
processor = 10
p = multiprocessing.Pool(processor)
for i,item in enumerate(chunks(name_pubs,28000)):
p.apply_async(disambiguate, args=(item,i,))
p.close()
p.join()
print('程序 总用时 :',datetime.now()-start_time)
四.python迭代器
4.1 python迭代器简介(主要是相关概念的理解)
我们已经知道,可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的generator function。这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
- 生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
- 生成器一定是迭代器对象;
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator;
- list、dict、str,tuple,set等虽然是Iterable,却不是Iterator对象;
- 把list、dict、str等Iterable变成Iterator可以使用iter()函数;
- 凡是可作用于for循环的对象都是Iterable类型;
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- Iterator对象是Iterable对象,而Iterable对象不一定是Iterator对象;
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
4.2 为什么list、dict、str等数据类型不是Iterator?
因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。 总结:可迭代对象(Iterable)一般序列长度都是已知的,而迭代器对象不能提前知道序列的长度,Iterator可以表示一个无限大的数据流,一直迭代下去。
|