2010-07-05 29 views
7

我想將生成器或迭代器遞歸轉換爲列表。
我在下面寫了一段代碼,但它看起來很幼稚和醜陋,並且可能會在doctest中丟棄大小寫。如何將生成器或迭代器遞歸轉換爲列表

Q1。幫助我的好版本。
Q2。如何指定對象是不可變的?

import itertools 

def isiterable(datum): 
    return hasattr(datum, '__iter__') 

def issubscriptable(datum): 
    return hasattr(datum, "__getitem__") 

def eagerlize(obj): 
    """ Convert generator or iterator to list recursively. 
    return a eagalized object of given obj. 
    This works but, whether it return a new object, break given one. 

    test 1.0 iterator 

    >>> q = itertools.permutations('AB', 2) 
    >>> eagerlize(q) 
    [('A', 'B'), ('B', 'A')] 
    >>> 

    test 2.0 generator in list 

    >>> q = [(2**x for x in range(3))] 
    >>> eagerlize(q) 
    [[1, 2, 4]] 
    >>> 

    test 2.1 generator in tuple 

    >>> q = ((2**x for x in range(3)),) 
    >>> eagerlize(q) 
    ([1, 2, 4],) 
    >>> 

    test 2.2 generator in tuple in generator 

    >>> q = (((x, (y for y in range(x, x+1))) for x in range(3)),) 
    >>> eagerlize(q) 
    ([(0, [0]), (1, [1]), (2, [2])],) 
    >>> 

    test 3.0 complex test 

    >>> def test(r): 
    ...  for x in range(3): 
    ...   r.update({'k%s'%x:x}) 
    ...   yield (n for n in range(1)) 
    >>> 
    >>> def creator(): 
    ...  r = {} 
    ...  t = test(r) 
    ...  return r, t 
    >>> 
    >>> a, b = creator() 
    >>> q = {'b' : a, 'a' : b} 
    >>> eagerlize(q) 
    {'a': [[0], [0], [0]], 'b': {'k2': 2, 'k1': 1, 'k0': 0}} 
    >>> 

    test 3.1 complex test (other dict order) 

    >>> a, b = creator() 
    >>> q = {'b' : b, 'a' : a} 
    >>> eagerlize(q) 
    {'a': {'k2': 2, 'k1': 1, 'k0': 0}, 'b': [[0], [0], [0]]} 
    >>> 

    test 4.0 complex test with tuple 

    >>> a, b = creator() 
    >>> q = {'b' : (b, 10), 'a' : (a, 10)} 
    >>> eagerlize(q) 
    {'a': ({'k2': 2, 'k1': 1, 'k0': 0}, 10), 'b': ([[0], [0], [0]], 10)} 
    >>> 

    test 4.1 complex test with tuple (other dict order) 

    >>> a, b = creator() 
    >>> q = {'b' : (b, 10), 'a' : (a, 10)} 
    >>> eagerlize(q) 
    {'a': ({'k2': 2, 'k1': 1, 'k0': 0}, 10), 'b': ([[0], [0], [0]], 10)} 
    >>> 

    """ 
    def loop(obj): 
     if isiterable(obj): 
      for k, v in obj.iteritems() if isinstance(obj, dict) \ 
         else enumerate(obj): 
       if isinstance(v, tuple): 
        # immutable and iterable object must be recreate, 
        # but realy only tuple? 
        obj[k] = tuple(eagerlize(list(obj[k]))) 
       elif issubscriptable(v): 
        loop(v) 
       elif isiterable(v): 
        obj[k] = list(v) 
        loop(obj[k]) 

    b = [obj] 
    loop(b) 
    return b[0] 

def _test(): 
    import doctest 
    doctest.testmod() 

if __name__=="__main__": 
    _test() 
+0

它是否必須是遞歸解決方案?爲什麼? – 2010-07-05 06:14:07

+0

儘量避免使用長度超過80個字符的行。當您必須使用水平滾動時,閱讀代碼非常不舒服。 – 2010-07-05 06:25:41

+0

謝謝,大衛 因爲我沒有注意到。 那麼,環路解決方案呢? – unacowa 2010-07-05 09:56:20

回答

5

爲了避免嚴重影響原來的對象,你基本上需要的copy.deepcopy變種......巧妙地調整了,因爲你需要打開發電機和迭代器列表(deepcopy的不會deepcopy的發電機反正)。注意一些對原始對象的影響不幸是不可避免的,因爲生成器和迭代器作爲遍歷它們的副作用(無論是將它們變成列表或用於任何其他目的)的「副作用」 - 因此,根本沒有辦法可以離開原來的對象單獨有那個生成器或其他迭代器變成了「variant-deepcopied」結果中的列表。

不幸的是,copy模塊沒有被編寫爲自定義模塊,因此替代方法是複製粘貼編輯,或者在私有模塊變量_deepcopy_dispatch(雙嘆)上纏繞的細微(嘆息)猴子補丁意味着你的補丁版本可能無法通過Python版本升級,假設從2.6到2.7)。另外,在每次使用eagerize(爲了避免影響deepcopy的其他用途)之後,猴子補丁將不得不被卸載。所以,我們假設我們選擇複製粘貼編輯路線。

假設我們從最新版本開始,那是在線的here。當然,你需要重命名模塊;在行145處將外部可見函數deepcopy重命名爲eagerize;實質性改變的是在161-165線,這說的版本,註釋,分別是:

161 :    copier = _deepcopy_dispatch.get(cls) 
162 :    if copier: 
163 :     y = copier(x, memo) 
164 :    else: 
165 : tim_one 18729   try: 

我們需要線163和164的邏輯之間插入「否則,如果它的迭代擴大到列表(即, 。使用功能_deepcopy_list作爲複印機」因此,這些行成爲:

161 :    copier = _deepcopy_dispatch.get(cls) 
162 :    if copier: 
163 :     y = copier(x, memo) 
        elif hasattr(cls, '__iter__'): 
         y = _deepcopy_list(x, memo) 
164 :    else: 
165 : tim_one 18729   try: 

這就是全部。只是有兩個添加的行注意,我獨自離開原來的行號,使之清透其中準確這兩行需要插入,而不是編號爲兩個新行,還需要重新命名其他行標識符deepcopy(間接遞歸調用)到eagerize的實例。

您還應該刪除第66-144行(您不關心的淺拷貝功能)並適當調整第1-65行(文檔,導入,__all__等)。

當然,你想工作過的明文版本的copy.pyhere,沒有註釋的版本,我已經提到(我用的是註釋版本只是爲了澄清什麼地方是需要更改的副本! - )。

+1

哇,我們很榮幸能有你在這裏亞歷克斯! – fmark 2010-07-05 07:22:12

+0

謝謝,這是格雷的想法alex。它真的很有幫助。 我測試了它並且deepcopy工作,但測試3.x 4.x失敗。 ,因爲發電機測試(r)對字母r有副作用。 非常抱歉在文檔字符串的頂部混淆了表達式。 我需要副作用。字典必須改變。 – unacowa 2010-07-05 09:46:42