2010-07-05 6 views
7

Quiero convertir el generador o el iterador en una lista recursiva.
Escribí un código a continuación, pero parece ingenuo y feo, y puede que se caiga el caso en doctest.Cómo convertir un generador o un iterador a una lista recursiva

Q1. Ayudame una buena version
Q2. ¿Cómo especificar el objeto es inmutable o no?

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

¿Tiene que ser una solución recursiva? ¿Por qué? –

+0

Intenta evitar las líneas de más de 80 caracteres. Es muy incómodo leer el código cuando debe usar desplazamiento horizontal. –

+0

Gracias, David Porque no me di cuenta. Entonces, ¿qué tal la solución de lazo? – unacowa

Respuesta

5

Para evitar mal que afecta al objeto original, que básicamente necesita una variante de copy.deepcopy ... sutilmente retocado porque es necesario para convertir generadores e iteradores en listas (deepcopy no lo haría generadores DeepCopy de todos modos). Tenga en cuenta que un poco de efecto en el objeto original es desafortunadamente inevitable, porque los generadores e iteradores están "agotados" como un efecto secundario de iterar todo el camino en ellos (ya sea para convertirlos en listas o para cualquier otro propósito) - por lo tanto , simplemente no hay forma de que pueda ambos dejar el objeto original solo y tienen ese generador u otro iterador convertido en una lista en el resultado "variante-profunda".

El módulo copy es, por desgracia no está escrito para ser personalizado, para que el ares alternativas, ya sea copiar y pegar a editar, o una sutil (suspiro) mono-parche de articulación en (doble suspiro) la variable de módulo privado _deepcopy_dispatch (que significa que su versión parchada podría no sobrevivir a una actualización de la versión Python, digamos de 2.6 a 2.7, hipotéticamente). Además, el parche de monos debería desinstalarse después de cada uso de su eagerize (para evitar afectar otros usos de deepcopy). Entonces, supongamos que escogemos la ruta copiar-pegar-editar en su lugar.

Digamos que comenzamos con la versión más reciente, la que está en línea here. Necesita cambiar el nombre del módulo, por supuesto; cambie el nombre de la función externa visible deepcopy al eagerize en la línea 145; el cambio sustancial es en las líneas 161-165, que en dicha versión, anotada, son:

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

Necesitamos insertar entre la línea 163 y 164 de la lógica "de lo contrario si es iterable expanda a una lista (es decir, . utilizar la función _deepcopy_list como la copiadora" Así pues, estas líneas se convierten en:

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: 

eso es todo:. sólo hay dos líneas agregó una nota que he dejado los números de línea originales solo para que sea perfectamente claro donde exactamente estas dos líneas deben insertarse y no deben numerarse las dos nuevas líneas. También debe cambiar el nombre de otras líneas. instancias del identificador deepcopy (llamadas recursivas indirectas) al eagerize.

También debe eliminar las líneas 66-144 (la funcionalidad de copia superficial que no le importa) y ajustar adecuadamente las líneas 1-65 (docstrings, importaciones, __all__, etc.).

Por supuesto, usted quiere trabajar de una copia de la versión en texto plano de copy.py, here, no la versión anotada he estado refiriendo a (he utilizado la versión comentada sólo para aclarar exactamente donde se necesitan los cambios ! -).

+1

Guau, ¡tenemos suerte de tenerte aquí, Alex! – fmark

+0

Gracias, es una idea genial Alex. Realmente me ayudó. Lo probé y Deepcopy funciona, pero la prueba 3.x 4.x falla. porque la prueba del generador (r) tiene efectos secundarios para dict r. Lo siento mucho por la expresión confusa en la parte superior de docstring. Necesito efectos secundarios. dict r debe ser cambiado. – unacowa

Cuestiones relacionadas