python分块处理功能,python 数据分段
在需求的过程中,有一个分块处理大量数据的场景,具体来说就是几十万条数据,分批次处理,每次处理100条。这个时候,你就需要一个具有阻断功能的代码。本文为您分享了Python中一种优雅的数据分区方法。有需要可以参考一下。
00-1010 1.背景2.islice2.1示例2.2仅指定步长3.iter3.1一般使用3.2高级使用4 .iSlice和iter 5的组合。摘要
目录
看到这个标题,你可能会想,屏蔽能有多难?还值得细说吗?最近确实碰到一个很有意思的块函数,写的很巧妙很优雅,就写了一个来分享。
前几天在需求制作的过程中,出现了大量数据分块处理的场景,具体来说就是批量处理几十万条数据,每次处理100条。这时候你需要一个块功能代码,而项目的工具库中正好有一个块功能。用了功能,发现还挺好用的。我传入了列表和块大小,然后我可以遍历并取出划分的数据。呼叫模式如下:
从xxx导入chunk_fun
Chunk_list=chunk_fun(arr,100) #分块数据,指定块的大小为100。
对于chunk_list:中的块
打印(块)
然后就对这个block函数产生了兴趣,想看看这个小函数是怎么实现的。如果我写一个块函数,我知道Python中的range函数可以指定步长,这个特性完全可以优雅地实现块函数。
arr=[1,2,3,4,5,6,7,8,9,10]
步骤=3
对于范围(0,len(arr),步长):内的I
chunk=arr[i:i步骤]
打印(块)
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
没想到看到源代码只用了三行代码就实现了分区,不仅支持列表等线性结构的分区,还支持集合等非线性结构的分区。这让我感到震惊。这3行代码不是最优雅的阻塞方法,但也接近最优雅的阻塞方法。事不宜迟,先编写代码:
从itertools导入islice
定义块列表(it,limit):
it=iter(it)
返回iter(lambda: list(islice(it,limit)),[])
对于这三行代码,有多少人第一眼没看出来功能?反正我第一眼看到的就是一脸懵,有种不省人事的感觉。
我们先来看看这个阻塞函数的用法。
set_num={1,2,3,4,5,6,7}
对于chunk_list(set_num,2):中的temp_list
打印(临时列表)
[1, 2]
[3, 4]
[5, 6]
[7]
分区安排得很清楚,根本没有使用显示循环,只用了三行代码。只有两行代码没有定义函数。这是我见过的最优雅的分区方式。然后我会花一点时间弄清楚代码是如何工作的。
那么这个组块功能是如何实现的呢?主要有两个知识点:迭代器片islice迭代器生成函数iter。通过这两个功能的配合,完成阻断功能。下面我将详细介绍这两种方法的使用。
1.背景
Islice是python内置模块itertool中的一个函数。它的功能是切片迭代器,传入一个迭代器,返回迭代器中从起始位置到终止位置的元素,默认起始位置。
该函数定义如下:
islice(可重复,[开始,]停止[,步进])
可迭代的迭代对象开始切片开始位置停止切片结束位置步进步进步进
2.islice
示例
from itertools import islicefrom collections import Iterator
iter_list = iter([1,2,3,4,5,6,7])
slice = islice(iter_list, 0, 7, 2)
print(slice)
>>>
<itertools.islice object at 0x7fc864e5aef8>
print(isinstance(slice, Iterator))
>>>
True
print(list(slice))
>>>
[1, 3, 5, 7]
指定start为0,stop为7,step2,得到一个新的迭代器,元素是从1开始的步长为2取到的数据。
2.2只指定步长
islice可以只传入步长参数,当没有start和stop时,默认从start为起点,stop为终点。
from itertools import isliceiter_list = iter([1,2,3,4,5,6,7])
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
slice = islice(iter_list, 2)
print(list(slice))
>>>
[1, 2]
[3, 4]
[5, 6]
[7]
[]
[]
[]
除了获得切片之外,以上代码还说明了两个非常重要的特征,是否有留意?
第一个:那就是切片能够保留位置信息,多次调用切片功能,当前取值是从上一次结尾的地方开始的。比如第一次取值1、2,结尾位置是3;第二次就从3开始取到了3、4;第三次从5开始取到5、6。原因islice是对迭代器切片,迭代器取值会记住位置信息。
第二个:当迭代完所有的元素之后,返回空数组。将原始列表迭代完之后不会报错,而是一直返回空数组。
有了上面这种使用方法就为分块提供了可能性,如果要使用islice来分块,只需要在一个死循环里调用islice取值,当取值为[]
时退出循环即可。可通过如下方法实现:
from itertools import islicedef chunk(it, limit):
it = iter(it)
while True:
temp = list(islice(it, limit))
if temp == []:
break
yield temp
iter_list = iter([1,2,3,4,5,6,7])
for temp_list in chunk(iter_list, 2):
print(temp_list)
>>>
[1, 2]
[3, 4]
[5, 6]
[7]
这样就完成了使用islice就完成了分块的功能,但是看上可不是很优雅,又有while循环,又有yield关键值。
不优雅关键在于需要循环调用切片函数而且还需要判断跳出循环的条件。那么有没有一个既可以循环调用又能判断结束条件的函数呢?还真的有的,那就是iter
。
3.iter
iter()方法用来创建迭代器,iter()本质上就是调用可迭代对象的__iter__
方法,返回一个迭代器对象。关于iter的常规使用,可参见另一篇文章一篇文章讲清楚迭代器和生成器
3.1常规使用
常见的iter的使用方法是,对一个可迭代对象调用iter方法,让其变成一个迭代器,可以通过next取值。
list = [1,2,3,4,5,6,7]iter_list = iter(list)
print(next(iter_list))
print(next(iter_list))
print(next(iter_list))
>>>
1
2
3
3.2进阶使用
iter还有一种不常用的方法,来看iter函数的定义
iter(object[, sentinel])
- object -- 支持迭代的集合对象。
- sentinel -- 如果传递了第二个参数,则参数 object 必须是一个可调用的对象(如,函数),此时,iter 创建了一个迭代器对象,每次调用这个迭代器对象的__next__()方法时,都会调用 object。
也就是说如果iter函数如果传了第二个参数,那么第一个参数就必须是一个可调用对象,每一次调用next函数时,实际上就是调用第一个参数,如果结果等于第二个参数,那就是迭代完成了。
听起来有点弯弯绕,跑一个示例就清楚了。
import randomdef get_random():
return random.randint(1,5)
demo = iter(get_random, 4)
print(next(demo))
print(next(demo))
print(next(demo))
print(next(demo))
print(next(demo))
print(next(demo))
print(next(demo))
>>>
3
2
1
2
Traceback (most recent call last):
File "islice_demo.py", line 62, in <module>
print(next(demo))
StopIteration
iter传入第一个参数是一个函数get_random,函数的功能是获取1-5之间的随机数,第二个参数是4,也就是说如果函数返回的数值是4,那算迭代完成。每一次调用next取值就会调用get_random函数,直到结果为4。当迭代完成之后,会抛出一个StopIteration
的异常。
上面是通过next调用,如果是通过for循环调用,就不会抛出异常,for循环会捕获异常。
import randomdef get_random():
return random.randint(1,5)
demo = iter(get_random, 4)
for i in demo:
print(i)
>>>
1
5
这个功能刚好可以实现调用某一个函数,又能判断退出条件,如果现在再把分块的代码摆上来,能否实现优雅的分块呢?
from itertools import islicedef chunk(it, limit):
it = iter(it)
while True:
temp = list(islice(it, limit))
if temp == []:
break
yield temp
iter_list = iter([1,2,3,4,5,6,7])
for temp_list in chunk(iter_list, 2):
print(temp_list)
4.islice 和 iter 组合使用
islice 提供分块功能,iter 提供循环调用islice的功能和判断退出的功能,最后在两个函数的的配合使用下,完成了优雅的分块。
便于理解的示例:
from itertools import islicedef chunk_list(it, limit):
it = iter(it)
# 实现分块的内函数
def iter_fun():
return list(islice(it, limit))
return iter(iter_fun, [])
it = [1,2,3,4,5,6,7]
chunk = chunk_list(it, 2)
print(next(chunk))
print(next(chunk))
print(next(chunk))
print(next(chunk))
print(next(chunk))
>>>
[1, 2]
[3, 4]
[5, 6]
[7]
Traceback (most recent call last):
File "chunk_demo.py", line 44, in <module>
print(next(chunk))
StopIteration
最终的示例:
from itertools import islicedef chunk_list(it, limit):
it = iter(it)
return iter(lambda: list(islice(it, limit)), [])
iter 第一个参数传入lambda表达式,有一个更贴合场景的叫法是无头函数。lambda: list(islice(it, limit))
。没有传入参数,函数体是islice(it, limit)
;
第二个参数是空列表[],作为迭代退出的判断。
工作原理:
当使用for循环遍历分块函数时,每循环一次就通过iter调用islice一次,将分块结果list处理,然后返回。直到islice返回空列表,iter根据第二个参数判断退出循环。
5.总结
分块函数的优点:
- 实现很优雅
- 支持的分块的数据类型丰富。不单是列表,只要能够迭代的都可以。
分块的实现主要有两个思路:
- 使用islice来完成迭代器切片,实现分块的功能。但是需要多次调用islice直到迭代完成
- iter 提供调用功能,并判断迭代退出条件
有兴趣的读者可看看iter的实现,能够明白为什么迭代器能记住位置,这是本文分块的一个核心知识点。
这一个简单的代码让我感受到Python的奇妙,两个函数默契的配合,十分优雅的完成了分块功能。同时我明白Python语言的宗旨是简易优雅,但是简易并不简单,想要实现优雅需要扎实的基础和深厚的知识储备。追求Pythonic,需要学习理解的还有很多。
以上就是一个Python优雅的数据分块方法详解的详细内容,更多关于Python数据分块的资料请关注盛行IT软件开发工作室其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。