Iterar sobre una cadena de 2 (o n) caracteres a la vez en Python

Antes de hoy, tenía que recorrer una secuencia de 2 caracteres a la vez para analizar una cadena formateada como "+c-R+DE" (hay algunas letras adicionales).

Terminé con esto, que funciona, pero se ve feo. Terminé comentando lo que estaba haciendo porque no era obvio. Casi parece python, pero no del todo.

 # Might not be exact, but you get the idea, use the step # parameter of range() and slicing to grab 2 chars at a time s = "+c-R+De" for op, code in (s[i:i+2] for i in range(0, len(s), 2)): print op, code 

¿Hay alguna forma mejor / más limpia de hacer esto?

No sé sobre limpiador, pero hay otra alternativa:

 for (op, code) in zip(s[0::2], s[1::2]): print op, code 

Una versión sin copia:

 from itertools import izip, islice for (op, code) in izip(islice(s, 0, None, 2), islice(s, 1, None, 2)): print op, code 

Tal vez esto sería más limpio?

 s = "+c-R+De" for i in xrange(0, len(s), 2): op, code = s[i:i+2] print op, code 

Quizás puedas escribir un generador para hacer lo que quieras, tal vez eso sería más pythonico 🙂

Tríptico inspiró esta solución más general:

 def slicen(s, n, truncate=False): assert n > 0 while len(s) >= n: yield s[:n] s = s[n:] if len(s) and not truncate: yield s for op, code in slicen("+c-R+De", 2): print op,code 
 from itertools import izip_longest def grouper(iterable, n, fillvalue=None): args = [iter(iterable)] * n return izip_longest(*args, fillvalue=fillvalue) def main(): s = "+c-R+De" for item in grouper(s, 2): print ' '.join(item) if __name__ == "__main__": main() ##output ##+ c ##- R ##+ D ##- e 

izip_longest requiere Python 2.6 (o superior). Si está en Python 2.4 o 2.5, use la definición de izip_longest del documento o cambie la función de izip_longest a:

 from itertools import izip, chain, repeat def grouper(iterable, n, padvalue=None): return izip(*[chain(iterable, repeat(padvalue, n-1))]*n) 

Gran oportunidad para un generador. Para listas más grandes, esto será mucho más eficiente que comprimir todos los demás elementos. Tenga en cuenta que esta versión también maneja cadenas con op colgantes.

 def opcodes(s): while True: try: op = s[0] code = s[1] s = s[2:] except IndexError: return yield op,code for op,code in opcodes("+c-R+De"): print op,code 

edición: reescritura secundaria para evitar excepciones de ValueError.

Las otras respuestas funcionan bien para n = 2, pero para el caso general, puede intentar esto:

 def slicen(s, n, truncate=False): nslices = len(s) / n if not truncate and (len(s) % n): nslices += 1 return (s[i*n:n*(i+1)] for i in range(nslices)) >>> s = '+c-R+De' >>> for op, code in slicen(s, 2): ... print op, code ... + c - R + D - e >>> for a, b, c in slicen(s, 3): ... print a, b, c ... + c - R + D Traceback (most recent call last): File "", line 1, in ? ValueError: need more than 2 values to unpack >>> for a, b, c in slicen(s,3,True): ... print a, b, c ... + c - R + D 

Este enfoque admite un número arbitrario de elementos por resultado, evalúa perezosamente y la entrada iterable puede ser un generador (no se intenta indexar):

 import itertools def groups_of_n(n, iterable): c = itertools.count() for _, gen in itertools.groupby(iterable, lambda x: c.next() / n): yield gen 

Cualquier elemento sobrante se devuelve en una lista más corta.

Ejemplo de uso:

 for g in groups_of_n(4, xrange(21)): print list(g) [0, 1, 2, 3] [4, 5, 6, 7] [8, 9, 10, 11] [12, 13, 14, 15] [16, 17, 18, 19] [20] 
 >>> s = "+c-R+De" >>> s '+c-R+De' >>> s[::2] '+-+-' >>> 

Quizás no sea el más eficiente, pero si te gustan las expresiones regulares …

 import re s = "+c-R+De" for op, code in re.findall('(.)(.)', s): print op, code 

Me encontré con un problema similar. Terminé haciendo algo como esto:

 ops = iter("+c-R+De") for op in ops code = ops.next() print op, code 

Sentí que era lo más legible.

Aquí está mi respuesta, un poco más limpia a mis ojos:

 for i in range(0, len(string) - 1): if i % 2 == 0: print string[i:i+2] 

Considere la posibilidad de instalar pip more_itertools , que ya viene con una implementación chunked junto con otras herramientas útiles:

 import more_itertools for op, code in more_itertools.chunked(s, 2): print(op, code) 

Salida:

 + c - R + D - e