python itertool_函数式编程的Python实践(2):Itertool
我們的上一個文章從很質樸的角度分析了究竟如何實踐函數式的思想,但是由于Python是一個多范式的語言,既支持指令式,也支持函數式。但是,默認導入語言的函數只有map、filter和reduce(現在reduce也變成了庫的一部分,不是默認導入了)。如果想更加方便的發揮Python函數編程的能力,實現我們的思路,我們必須借助以下幾個內置庫:itertools, functools和operator。
無論是不是采用函數編程范式,熟悉這些工具對我們寫出高效、優美的程序都很有幫助。同時,手中的工具很可能也決定了思考問題的角度。就好比,不知道斧子的存在,手上只有一把小刀,那么很可能就只能用小刀砍樹了。這篇文章我們主要介紹Itertools。文章主要分成兩個部分:Iterator抽象
Itertool常用函數
1、Iterator的抽象
Python宏偉(嗎?)的函數編程大廈其實建立在Iterator之上,所以我們需要先弄清楚這個東西。
概念上,Iterator就是一個代表數據流的對象(object)。從語言的角度,Iterator是一種數據類型(Type),Type就會定義能做什么,或者說只要有特定的行為就可以稱為這個類型。Iterator類型定義了兩種行為:__iter__()和__next__()。iter返回一個Iterator對象(如果本身就是iIterator,那么就返回自己本身),next返回容器中下一個元素。一旦實現了這兩個函數,我們就可以任意使用Pyhton提供的任何針對Iterator的函數和功能了。比如 for loop以及我們接下來要討論的Itertools。同時,可以調用next(Iterator)來手動提取下一個元素。
Python里面內置最常見的Iterator實例就是Sequence,包括list, tuple, range。當然,Sequence不僅僅實現了Iterator的兩個功能,他們還實現了一些其他Sequence Type特有的行為,比如, a+b,a[i], len(a), max(a)等等。
另一個跟Iterator緊密兩連的類型是Generator。如果一個類的iter函數采用Generator來實現,那么它會自動返回一個Iterator(其實是Generator Object),而這個東西已經是Iterator 類型。
那么如何構造一個Generator呢?其實他跟通常的函數一樣,只不過采用yield作為返回,而不是return。每一次yield被調用的時候,函數會掛起整個進程,記錄當前狀態。當再一次被調用的時候,他會接著上面的狀態進入下一個狀態。通常Generator版本的Iterator會更加簡潔。
講了這么多理論,我們來看一個例子吧,如何實現一個自定一個數據結構,使他成為一個Iterator。
# 這兩個函數/類 實現了相同的功能。
class PowTwo:
def __init__(self, max = 0):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n > self.max:
raise StopIteration
result = 2 ** self.n
self.n += 1
return result
# 但是Generator的版本更加方便
def powGen(max = 0):
n = 0
while n < max:
yield 2**n
n += 1
# 你可以從一個list建相應的Iterator
it = iter([1, 2, 3])
# 你可以手動獲得下一個
nt = next(it)
nt = next(it)
nt = next(it)
我們實現Iterator主要有兩個目的:他們可以是惰性的,節約內存
他們可以被傳遞到所有支持Iterator的函數中,最終形成強大復雜的功能
好了,稍微總結一下,我們需要Iterator來進一步進行函數編程,Generator/yield提供了簡單的方式夠在Iterator。接下來是更加有趣的東西啦!
2、Itertool,高效操作容器
itertool主要提供常見的迭代器抽象。這些函數主要是為了模擬一些經典的函數式編程語言,比如Haskell,SML等等。比如,在SML語言中,存在一個tabulate(f),它實現f(0), f(1), ...的功能,在Python中可以通過組合map和count實現:map(f, count)。
Itertool主要包含三類:無限迭代,有限迭代和組合迭代。需要注意的是,無限迭代都是懶惰的(Lazy), 這樣在內存方面就會比較節省。
無限迭代包括三個函數:count(start, step), cycle(p)和repeat(elem, n) 。
count(10) ->10, 11, 12, .....
cycle('abcd') ->a, b, c, d, a, b, c, d, a, ....
repeat(10) ->10, 10, ...
有限迭代比較豐富。
# >>> accumulate: 帶過程的reduce
# 有點Reduce的感覺是不是?!但是保留了過程。你想過如何用reduce實現嗎?
accumulate([1,2,3], func=operator.add) # => [1,3,6]
cashflows = [1000, -90, -90, -90, -90]
list(accumulate(cashflows, lambda bal, pmt: bal*1.05 + pmt))
# [1000, 960.0, 918.0, 873.9000000000001, 827.5950000000001]
# >>> chain: append的升級版
chain('ABC', 'DEF')
#--> A B C D E F
# >>> compress: filter但是用預訂的條件
compress('ABCDEF', [1,0,1,0,1,1])
# --> A C E F
# >>> dropwhile: 另一個常用的filter
# 他會丟棄不滿足條件的元素,直到遇到一個滿足條件的,然后后面的元素就不在filter了
dropwhile(lambda x: x<5, [1,4,6,4,1])
# --> 6 4 1
# >>> filterfalse: 返回錯的!
filterfalse(lambda x: x%2, range(10))
# --> 0 2 4 6 8
# >>> groupby:連續的key分組
[k for k, g in groupby('AAAABBBCCDAABBB')] # --> A B C D A B
[list(g) for k, g in groupby('AAAABBBCCD')] # --> AAAA BBB CC D
# >>> islice: 高級索引
islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G
# >>> starmap: map元素的第一個元素
starmap(pow, [(2,5), (3,2), (10,3)]) # --> 32 9 1000
# >>> takewhile: 跟dropwhile反過來
takewhile(lambda x: x<5, [1,4,6,4,1]) # --> 1 4
# >>> tee: 生產很多個迭代器
# >>> zip_longest: 另一個版本的zip
zip_longest('ABCD', 'xy', fillvalue='-') # --> Ax By C- D-
下面我們看組合迭代函數
# >>> product: 柯西積, ((x,y) for x in A for y in B)
product('ABCD', 'xy') # --> Ax Ay Bx By Cx Cy Dx Dy
# >>> permutations
permutations(range(3)) # --> 012 021 102 120 201 210
permutations('ABCD', 2) # --> AB AC AD BA BC BD CA CB CD DA DB DC
# >>> combinations: 有序的permutation
combinations('ABCD', 2) # --> AB AC AD BC BD CD
好!基本的工具就是這么多了,下面我們來看看如果用他們構造更加高級的函數吧!因為上面的工具在底層進行了內存和計算方面的優化,我們利用他們構造的函數同樣集成了效率上的優勢。當我們開始使用這些工具時,我們的思考方式會讓我們的代碼更加精簡、優美,自動的呈現函數編程的模式:小工具連在一起,減少中間變量。同時,由于進行了向量化處理,我們代碼效率也會相應提高。
下面列舉一些非常常用的函數。
from itertools import *
from operator import itemgetter
# >>> take: Haskell的hello world.
def take(n, iteratable):
"Return first n items of the iterable as a list"
return list(islice(iterable, n))
# >>> tail: 同take
def tail(n, iteratable):
# tail(3, 'ABCDEFG') --> E F G
return iter(collections.deque(iterable, maxlen=n))
# >>> prepend: Haskell的 1::[2,3,4]
def prepend(value, iterator):
"Prepend a single value in front of an iterator"
# prepend(1, [2, 3, 4]) -> 1 2 3 4
return chain([value], iterator)
# >>> nth: 安全的索引, 你應該記得IndexException吧。。。
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(islice(iterable, n, None), default)
def all_equal(iterable):
# 返回True如果所有元素都相等
g = groupby(iterable)
return next(g, True) and not next(g, False)
# 從這里以后,我不寫注釋了,其實這些函數已經簡單到不用任何注釋了,
# 如果,不明白請打開terminal自己嘗試一下!很有趣的!
def ncycles(iterable, n):
"Returns the sequence elements n times"
return chain.from_iterable(repeat(tuple(iterable), n))
def flatten(listOfLists):
"Flatten one level of nesting"
return chain.from_iterable(listOfLists)
def padnone(iterable):
return chain(iterable, repeat(None))
def n_cycles(iterable, n):
return chain.from_iterable(repeat(tuple(iterable), n))
def repeatfunc(func, times=None, *args):
if times is None:
return starmap(func, repeat(args))
return starmap(func, repeat(args, times))
def powerset(iterable):
# powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
s = list(iterable)
return chain.from_iterable(combinators(s, r) for r in range(len(s) + 1))
def partition(pred, iterable):
'Use a predicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
def unique_everseen(it, key=None):
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for ele in filterfalse(seen.__contains__, it)
seen_add(ele)
yield ele
else:
for ele in it:
k = key(ele)
if k not in seen:
seen_add(k)
yield ele
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return map(next, map(itemgetter(1), groupby(iterable, key)))
def iter_except(func, exception, first=None):
""" Call a function repeatedly until an exception is raised.Converts a call-until-exception interface to an iterator interface.Like builtins.iter(func, sentinel) but uses an exception insteadof a sentinel to end the loop.Examples:iter_except(functools.partial(heappop, h), IndexError) # priority queue iteratoriter_except(d.popitem, KeyError) # non-blocking dict iteratoriter_except(d.popleft, IndexError) # non-blocking deque iteratoriter_except(q.get_nowait, Queue.Empty) # loop over a producer Queueiter_except(s.pop, KeyError) # non-blocking set iterator"""
try:
if first is not None:
yield first()
while True:
yield func()
except exception:
pass
def first_true(iterable, default=False, pred=None):
"""Returns the first true value in the iterable.If no true value is found, returns *default*If *pred* is not None, returns the first itemfor which pred(item) is true."""
# first_true([a,b,c], x) --> a or b or c or x
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
return next(filter(pred, iterable), default)
def random_product(*args, repeat=1):
"Random selection from itertools.product(*args, **kwds)"
pools = [tuple(pool) for pool in args] * repeat
return tuple(random.choice(pool) for pool in pools)
參考:
Change Log2019-11-10 完成第一版
總結
以上是生活随笔為你收集整理的python itertool_函数式编程的Python实践(2):Itertool的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos标准分区调整大小_去繁化简解
- 下一篇: python 小例子 源码 莫凡_100