string.split()
devuelve una instancia de lista . ¿Hay alguna versión que devuelva un generador en su lugar? ¿Hay alguna razón para no tener una versión de generador?
113
string.split()
devuelve una instancia de lista . ¿Hay alguna versión que devuelva un generador en su lugar? ¿Hay alguna razón para no tener una versión de generador?
split
la cadena y luego devolvió un generador trabajando en el resultado desplit
. Eso me hizo pensar si había una manera desplit
devolver un generador para empezar.Respuestas:
Es muy probable que
re.finditer
utilice una sobrecarga de memoria bastante mínima.Manifestación:
editar: Acabo de confirmar que esto requiere memoria constante en Python 3.2.1, asumiendo que mi metodología de prueba fuera correcta. Creé una cadena de tamaño muy grande (1GB más o menos), luego itere a través del iterable con un
for
bucle (NO una lista de comprensión, que habría generado memoria adicional). Esto no resultó en un crecimiento notable de la memoria (es decir, si hubo un crecimiento en la memoria, fue mucho menor que la cadena de 1GB).fuente
a_string.split("delimiter")
?str.split()
no acepta expresiones regulares, eso es lore.split()
que estás pensando ...La forma más eficiente en la que puedo pensar es escribir uno usando el
offset
parámetro delstr.find()
método. Esto evita mucho uso de memoria y depende de la sobrecarga de una expresión regular cuando no es necesaria.[editar 2016-8-2: actualizado esto para admitir opcionalmente separadores de expresiones regulares]
Esto se puede usar como quieras ...
Si bien hay un poco de búsqueda de costos dentro de la cadena cada vez que se realiza find () o corte, esto debería ser mínimo ya que las cadenas se representan como matrices continuas en la memoria.
fuente
Esta es una versión del generador de
split()
implementada víare.search()
que no tiene el problema de asignar demasiadas subcadenas.EDITAR: Se corrigió el manejo de los espacios en blanco circundantes si no se dan caracteres de separación.
fuente
re.finditer
?Hice algunas pruebas de rendimiento en los diversos métodos propuestos (no los repetiré aquí). Algunos resultados:
str.split
(predeterminado = 0.3461570239996945re.finditer
(respuesta de ninjagecko) = 0.698872097000276str.find
(una de las respuestas de Eli Collins) = 0,7230395330007013itertools.takewhile
(Respuesta de Ignacio Vazquez-Abrams) = 2.023023967998597str.split(..., maxsplit=1)
recursividad = N / A †† Las respuestas de recursividad (
string.split
conmaxsplit = 1
) no se completan en un tiempo razonable, dadostring.split
la velocidad, pueden funcionar mejor en cadenas más cortas, pero luego no puedo ver el caso de uso para cadenas cortas donde la memoria no es un problema de todos modos.Probado usando
timeit
en:Esto plantea otra pregunta sobre por qué
string.split
es mucho más rápido a pesar de su uso de memoria.fuente
Aquí está mi implementación, que es mucho, mucho más rápida y completa que las otras respuestas aquí. Tiene 4 subfunciones separadas para diferentes casos.
Solo copiaré la cadena de documentos de la
str_split
función principal :Divida la cadena
s
por el resto de los argumentos, posiblemente omitiendo partes vacías (empty
argumento de palabra clave es responsable de eso). Esta es una función de generador.Cuando solo se proporciona un delimitador, la cadena simplemente se divide por él.
empty
es entoncesTrue
por defecto.Cuando se proporcionan varios delimitadores, la cadena se divide por las secuencias más largas posibles de esos delimitadores de forma predeterminada o, si
empty
se establece enTrue
, también se incluyen cadenas vacías entre los delimitadores. Tenga en cuenta que los delimitadores en este caso pueden ser solo caracteres individuales.Cuando no se suministran delimitadores,
string.whitespace
se usa, por lo que el efecto es el mismo questr.split()
, excepto que esta función es un generador.Esta función funciona en Python 3, y se puede aplicar una solución fácil, aunque bastante fea, para que funcione en las versiones 2 y 3. Las primeras líneas de la función deben cambiarse a:
fuente
No, pero debería ser bastante fácil escribir uno usando
itertools.takewhile()
.EDITAR:
Implementación muy simple, medio rota:
fuente
takeWhile
. ¿Qué sería buenopredicate
para dividir una cadena en palabras (predeterminadosplit
) usandotakeWhile()
?string.whitespace
.'abc<def<>ghi<><>lmn'.split('<>') == ['abc<def', 'ghi', '', 'lmn']
No veo ningún beneficio obvio en una versión de generador desplit()
. El objeto generador tendrá que contener toda la cadena para iterar, por lo que no va a ahorrar memoria al tener un generador.Sin embargo, si quisieras escribir uno, sería bastante fácil:
fuente
id()
me corrigió. Y, obviamente, como las cadenas son inmutables, no necesita preocuparse de que alguien cambie la cadena original mientras está iterando sobre ella.Escribí una versión de la respuesta de @ ninjagecko que se comporta más como string.split (es decir, espacios en blanco delimitados por defecto y puedes especificar un delimitador).
Aquí están las pruebas que utilicé (tanto en python 3 como en python 2):
El módulo de expresiones regulares de python dice que hace "lo correcto" para los espacios en blanco Unicode, pero en realidad no lo he probado.
También disponible como esencia .
fuente
Si también desea poder leer un iterador (así como devolver uno), intente esto:
Uso
fuente
more_itertools.split_at
ofrece un análogo astr.split
para iteradores.more_itertools
es un paquete de terceros.fuente
itertools.chain
y evaluar resultados usando una comprensión de lista. Dependiendo de la necesidad y solicitud, puedo publicar un ejemplo.Quería mostrar cómo usar la solución find_iter para devolver un generador para delimitadores dados y luego usar la receta por pares de itertools para construir una siguiente iteración anterior que obtendrá las palabras reales como en el método de división original.
Nota:
fuente
Método más tonto, sin regex / itertools:
fuente
fuente
[f[j:i]]
y nof[j:i]
?aquí hay una respuesta simple
fuente