map
和filter
是Python中的两种高效函数,用于处理可迭代对象
。然而,如果你同时使用map
和filter
,代码会显得很乱。
list1 = [1, 2, 3, 4, 5, 6, 7]
str = list(map(lambda x: x * 2, filter(lambda x: x % 2 == 0, list1)))
print(str) # [4, 8, 12]
如果你能像下面这样使用管道|
在一个迭代器上应用多个方法,那不是很好吗?
from pipe import select, where
str2 = list(list1 | where(lambda x: x % 2 == 0) | select(lambda x: x * 2))
print(str2) # [4, 8, 12]
什么是Pipe?
Pipe
是一个Python库,使你能够在Python中使用管道。一个管道(|
)将一个方法的结果传递给另一个方法。
Pipe很受欢迎,因为它使我们的代码在对Python迭代器应用多种方法时看起来更干净。由于Pipe中的函数屈指可数,所以学习Pipe也非常容易。
首先需要安装Pipe:
pip install pipe
Where
迭代器中的过滤元素--
where
与SQL
类似,Pipe的where方法也可以用来过滤迭代表中的元素。
from pipe import where
str3 = list(list1 | where(lambda x: x % 2 == 0))
print(str3) # [2, 4, 6]
Select
将一个函数应用到一个迭代器上—select
select方法与map方法类似。select将一个方法应用于迭代器的每个元素。
在下面的代码中,我使用select将列表中的每个元素乘以2。
from pipe import select
str4 = list(list1 | select(lambda x: x * 2))
print(str4) # [2, 4, 6, 8, 10, 12, 14]
现在,你可能想知道:如果where
和select
的功能与map
和filter
相同,我们为什么还需要这些方法?
因为可以使用管道在另一个方法之后插入一个方法。因此,使用管道可以去除嵌套的小括号,使代码更容易阅读。
from pipe import select, where
s2 = list(list1
| where(lambda x: x % 2 == 0)
| select(lambda x: x * 2)
)
print(s2) # [4, 8, 12]
Chain
迭代序列的链路--
chain
处理嵌套的迭代器可能是一件很痛苦的事情。而我们可以使用chain来链接一连串的迭代变量。
from pipe import chain
arr = [[1, 2, [3, ]], [4, 5]]
str5 = list(arr | chain)
print(str5) # [1, 2, [3], 4, 5]
尽管应用链式后迭代器的嵌套程度降低了,我们仍然有一个嵌套的列表。要处理一个深度嵌套的列表,可以使用 traverse 来代替。
Traverse
递归展开迭代器--
traverse
traverse方法可以用来递归地展开迭代器。因此,你可以用这个方法把一个深度嵌套的列表变成一个一维列表。
from pipe import traverse
arr = [[1, 2, [3, ]], [4, 5]]
str6 = list(arr | traverse)
print(str6) # [1, 2, 3, 4, 5]
把这个方法和select
方法整合起来,以获得一个字典的值,并把列表扁平化。
from pipe import traverse, select
gr = [
{'name': 'Apple', 'Price': [2, 5]},
{'name': 'Orange', 'Price': 3},
{'name': 'Grape', 'Price': 6},
]
str7 = list(gr
| select(lambda g: g['Price'])
| traverse
)
print(str7) # [2, 5, 3, 6]
列表中的元素分组
有时,使用某个函数对列表中的元素进行分组可能是有用的。这可以用groupby
方法轻松实现。
为了看看这个方法是如何工作的,把一个数字列表变成一个字典,根据数字是偶数还是奇数来分组。
from pipe import groupby, select
str8 = list(
(1, 2, 3, 4, 5, 6, 7, 8, 9)
| groupby(lambda x: 'Even' if x % 2 == 0 else 'Odd')
| select(lambda x: {x[0]: list(x[1])})
)
print(str8) # [{'Even': [2, 4, 6, 8]}, {'Odd': [1, 3, 5, 7, 9]}]
在上面的代码中,我们使用groupby
将数字分组为Even
组和Odd
组。应用这个方法后的输出看起来像下面这样:
[('Even', <itertools._grouper at 0x7fbea8030550>),
('Odd', <itertools._grouper at 0x7fbea80309a0>)]
接下来,我们使用select
将一个元组变成一个字典的列表,其键是元组中的第一个元素,值是元组中的第二个元素。
[{'Even': [2, 4, 6, 8]},
{'Odd': [1, 3, 5, 7, 9]}]
为了只获得大于2的值,我们可以在选择方法中加入where
方法:
from pipe import groupby, select, where
str9 = list(
(1, 2, 3, 4, 5, 6, 7, 8, 9)
| groupby(lambda x: 'Even' if x % 2 == 0 else 'Odd')
| select(lambda x: {x[0]: list(x[1] | where(lambda x: x > 2))})
)
print(str9) # [{'Even': [4, 6, 8]}, {'Odd': [3, 5, 7, 9]}]
请注意,输出中不再有
2
和1
。
Dedup
使用一个键来重复取值--
dedup
dedup方法删除列表中的重复部分。
from pipe import dedup
L2 = [2, 1, 4, 5, 7, 5, 6, 5, 7, 8, 5, 4, 1, 2, 5, 3, 6, 5, 4, 5, 8, 5, 4, 8, 6, 2, 4, 7, 9]
L3 = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 4, 4, 2, 4, 1]
str10 = list(L2 | dedup)
str11 = list(L3 | dedup)
print(str10) # [2, 1, 4, 5, 7, 6, 8, 3, 9]
print(str11) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
这听起来可能没什么意思,因为set
方法可以做同样的事情。然而,这种方法更加灵活,因为它使你能够使用一个键获得独特的元素。
例如,你可以用这个方法来获得一个小于5
的唯一元素
和另一个大于
或等于5
的唯一
元素。
str11 = list(L3 | dedup(lambda key: key < 5))
print(str11) # [1, 5]
现在,把这个方法与 select
和 where
结合起来,得到一个有重复键和 None
值的字典的值。
from pipe import dedup, select, where
data = [
{'name': 'Apple', 'Count': 2},
{'name': 'Orange', 'Count': 4},
{'name': 'Grape', 'Count': None},
{'name': 'Orange', 'Count': 7},
]
str12 = list(
data
| dedup(key=lambda g: g['name'])
| select(lambda g: g['Count'])
| where(lambda count: isinstance(count, int))
)
print(str12) # [2, 4]
在上面的代码中,我们:
-
移除同名的元素;
-
获得count的值;
-
只选择整数的值。
在几行代码中,我们可以将多个方法应用于一个迭代器,同时仍然保持代码的简洁。
总结
本文中,None和大家一起学会了如何使用管道来保持你的代码的简洁和简短。
我希望这篇文章能给你带来知识,把对迭代器的复杂操作
变成一行简单的代码
。
参考资料
[1] Pipe: https://github.com/JulienPalard/Pipe