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?
splitla cadena y luego devolvió un generador trabajando en el resultado desplit. Eso me hizo pensar si había una manera desplitdevolver un generador para empezar.Respuestas:
Es muy probable que
re.finditerutilice 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
forbucle (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
offsetpará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.splitconmaxsplit = 1) no se completan en un tiempo razonable, dadostring.splitla 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
timeiten:Esto plantea otra pregunta sobre por qué
string.splites 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_splitfunción principal :Divida la cadena
spor el resto de los argumentos, posiblemente omitiendo partes vacías (emptyargumento 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.
emptyes entoncesTruepor 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
emptyse 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.whitespacese 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 buenopredicatepara 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_atofrece un análogo astr.splitpara iteradores.more_itertoolses un paquete de terceros.fuente
itertools.chainy 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