2016-04-23 79 views
3

爲了避免混淆,讓我定義將迭代器返回函數轉換爲「合適的迭代」返回函數的標準方法?

適當的迭代:一個迭代的對象不是一個迭代器。


問:不Python的標準庫已經提供了一個方法來轉換一個「迭代器的返回功能」變成了「適當的迭代 -returning功能」?


我以爲我看到了這個地方,但現在我找不到它。特別是,我掃描了文檔itertools,但沒有發現它。


FWIW,這個土生土長的實施似乎工作:

def to_iterable_maker(iterator_maker): 

    def iterable_maker(*args, **kwargs): 
     class nonce_iterable(object): 
      def __iter__(self): 
       return iterator_maker(*args, **kwargs) 
     return nonce_iterable() 

    return iterable_maker 

...但一次性nonce_iterable類在那裏看起來笨拙的我。我確信從標準庫中實現這樣的事情會好很多。


@Nikita

試試這個:

import itertools 

base = range(3) 
poops_out = itertools.permutations(base) 

print list(poops_out) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(poops_out) 
# [] 

myperms = to_iterable_maker(itertools.permutations) 
keeps_going = myperms(base) 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

通過itertools.permutationsto_iterable_maker(itertools.permutations)返回的值之間的差異。我的問題是:標準庫是否已經提供類似於to_iterable_maker的東西?

+0

你似乎困惑,python中的所有迭代器都是可迭代的。 '__iter__'方法是一個身份函數的事實並不重要。你是否試圖創建一個迭代器,它可以多次迭代**? – ppperry

+3

是否有一個具體的問題,你試圖解決這個問題? – jonrsharpe

+0

您的「可迭代製造商」將一遍又一遍地調用原始迭代器。另外,也許你可以使用['itertools.tee'](https://docs.python.org/3/library/itertools.html#itertools.tee)? –

回答

0

它沒有任何意義 - 「迭代器返回函數」到「可迭代返回函數」。如果一個函數返回一個迭代器,那麼它已經返回一個迭代器,因爲迭代器是迭代器,因爲它們需要有__iter__方法。

the docs

迭代

能夠一次返回其成員的一個的目的。 迭代示例包括您使用__iter__()或__getitem__()方法定義的任何類的所有序列類型(如list,str和元組) 以及某些非序列類型(如字典,文件對象和 )。 可以在for循環中以及需要 序列(zip(),map(),...)的許多其他地方使用應用程序。當一個可迭代對象 作爲參數傳遞給內建函數iter()時,它將返回該對象的一個​​ 迭代器。此迭代器適用於通過 值集的一次傳遞。在使用迭代器時,通常不需要調用iter()或自己處理迭代器對象。for語句 會爲您自動執行此操作,從而創建臨時未命名變量 以在循環的持續時間內保存迭代器。另請參閱迭代器, 序列和發生器。

迭代

表示數據流的對象。重複調用 迭代器的__next__()方法(或將它傳遞給內置函數 next())將返回流中的連續項。當沒有更多的數據可用時,則會引發StopIteration異常。此時,迭代器對象已耗盡,並且任何進一步調用其方法 ()方法都會再次引發StopIteration。

迭代器都要求有一個返回迭代器對象本身的__iter__()方法,以便每個迭代器也可迭代

,並且可以在其他iterables被接受的大多數地方使用。一個 值得注意的例外是嘗試多次迭代過程的代碼。 A容器對象(例如列表)產生一個新的迭代器,每個迭代器將其傳遞給iter()函數或在for循環中使用它。 使用迭代器嘗試執行此操作將返回與先前迭代過程中使用的迭代器對象相同的耗盡 ,使其看起來像空容器一樣​​顯示爲 。

UPD

我的意思是......

案例1(按步驟比較顯示):

  1. f = to_iterable_maker(iterator_maker);
  2. i = f(some_var),inonce_iterable__iter__;
  3. j = iter(i)j是由iterator_maker(some_var)返回的迭代器;
  4. next(j),返回一些值取決於some_var

案例2

  1. f = iterator_maker;
  2. i = f(some_var),i是等於iterator_maker(some_var)的迭代器,其具有__iter__(每迭代器協議);
  3. j = iter(i)j是迭代器iterator_maker(some_var)返回,因爲迭代器調用__iter__返回本身,所以j is i回報true;
  4. next(j),返回一些值取決於some_var

正如您所看到的,除了準備步驟中的其他複雜情況外,沒有什麼變化。

也許你可以提供關於你想通過這樣的'包裝'來實現什麼的額外信息,以瞭解真正的問題。

根據你的問題,我不能想到任何庫函數,這將把迭代器變成可迭代的,因爲它已經是了。如果你想重複迭代器,可以看看itertools.tee()

UPD2

所以,現在我明白了,我們的目標是轉換單通迭代多遍​​迭代器...

我的回答:

「標準庫已經提供了類似於 to_iterable_maker的東西嗎?」

是「否」。但最接近的是itertools.tee(),它可以幫助你將單個迭代器克隆成幾個,你可以在之後使用。關於你提到的例子:

import itertools 

base = range(3) 
poops_out = itertools.permutations(base) 

iterators = itertools.tee(poops_out, 4) 

#You shouldn't use original iterator after clonning, so make it refer to a clone 
#to be used again, otherwise ignore the following line 
poops_out, iterators = iterators[0], iterators[1:] 

for it in iterators: 
    print list(it) 

#Prints: 
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 
#[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

另一種常見的方式從迭代器獲取迭代是使用list()tuple()將它轉換,這將使多道:

import itertools 

base = range(3) 

poops_out = itertools.permutations(base) 

#Obviously poops_out gets consumed at the next line, so it won't iterate anymore 
keeps_going = tuple(poops_out) 

print list(poops_out) 
# [] 

print list(poops_out) 
# [] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

上述兩種方法都可能是沉重的內存使用情況,所以有時候這不是一個選項。在這種情況下,您遇到的解決方案將運行良好。另一個implemetation,這是我能想到的,並且是多一點點的面向對象的,但在其他方面沒有太大的不同,你的:

class IterableMaker(object): 
    '''Wraps function returning iterator into "proper" iterable''' 

    def __init__(self, iterator_maker): 
     self.f = iterator_maker 

    def __call__(self, *args, **kwargs): 
     self.args = args 
     self.kwargs = kwargs 
     return self 

    def __iter__(self): 
     return self.f(*self.args, **self.kwargs) 

使用是一樣的:

import itertools 

class IterableMaker(object): 

    def __init__(self, iterator_maker): 
     self.f = iterator_maker 

    def __call__(self, *args, **kwargs): 
     self.args = args 
     self.kwargs = kwargs 
     return self 

    def __iter__(self): 
     return self.f(*self.args, **self.kwargs) 

base = range(3) 
poops_out = itertools.permutations(base) 

print list(poops_out) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(poops_out) 
# [] 

my_perm = IterableMaker(itertools.permutations) 

keeps_going = my_perm(base) 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 

print list(keeps_going) 
# [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)] 
+0

根據帝斯曼的建議,我已經更新了我的問題,以便將原始腳註「升級」到帖子主體。請閱讀此更新。另請參閱我提供的代碼。即使你反對我的問題的措辭,我提供的代碼是明確的。問題歸結爲:標準庫是否已經有了一個函數來執行'to_iterable_maker'的功能,或者以其他方式支持這個功能? – kjo

+0

@kjo,因爲我看到它正在嘗試包裝和迭代器以使其可迭代。請參閱答案更新。 – Nikita

+0

我編輯了我的帖子,只爲你。 – kjo