string.split()
retorna uma instância de lista . Existe uma versão que retorna um gerador em vez disso? Há alguma razão para não ter uma versão do gerador?
113
string.split()
retorna uma instância de lista . Existe uma versão que retorna um gerador em vez disso? Há alguma razão para não ter uma versão do gerador?
split
da string e, em seguida, retornou um gerador trabalhando no resultado desplit
. Isso me fez pensar se havia uma maneira desplit
devolver um gerador para começar.Respostas:
É altamente provável que
re.finditer
use um overhead mínimo de memória.Demo:
editar: Acabei de confirmar que isso leva memória constante no python 3.2.1, assumindo que minha metodologia de teste estava correta. Eu criei uma string de tamanho muito grande (1 GB ou mais), em seguida, iterou por meio do iterável com um
for
loop (NÃO uma compreensão de lista, o que teria gerado memória extra). Isso não resultou em um crescimento perceptível da memória (ou seja, se houve um aumento na memória, era muito menor do que a string de 1 GB).fonte
a_string.split("delimiter")
?str.split()
não aceita expressões regulares, é o quere.split()
você está pensando ...A maneira mais eficiente que posso pensar é escrever um usando o
offset
parâmetro dostr.find()
método. Isso evita muito uso de memória e depende da sobrecarga de uma regexp quando ela não é necessária.[editar 2016-8-2: atualizado para suportar opcionalmente separadores regex]
Isso pode ser usado como você quiser ...
Embora haja um pouco de custo de busca dentro da string cada vez que find () ou fatiamento for executado, isso deve ser mínimo, pois as strings são representadas como matrizes contíguas na memória.
fonte
Esta é a versão do gerador do
split()
implementado viare.search()
que não tem o problema de alocar muitas substrings.EDIT: Tratamento corrigido de espaços em branco circundantes se nenhum caractere separador for fornecido.
fonte
re.finditer
?Fiz alguns testes de desempenho nos vários métodos propostos (não os repetirei aqui). Alguns resultados:
str.split
(padrão = 0,3461570239996945re.finditer
(resposta de ninjagecko) = 0,698872097000276str.find
(uma das respostas de Eli Collins) = 0,7230395330007013itertools.takewhile
(Resposta de Ignacio Vazquez-Abrams) = 2,023023967998597str.split(..., maxsplit=1)
recursão = N / A †† As respostas de recursão (
string.split
commaxsplit = 1
) não são concluídas em um tempo razoável, dadostring.split
a velocidade s, elas podem funcionar melhor em strings mais curtas, mas não consigo ver o caso de uso para strings curtas em que a memória não seja um problema de qualquer maneira.Testado usando
timeit
em:Isso levanta outra questão: por que
string.split
é tão mais rápido apesar do uso de memória.fonte
Aqui está minha implementação, que é muito, muito mais rápida e completa do que as outras respostas aqui. Possui 4 subfunções separadas para casos diferentes.
Vou apenas copiar a docstring da
str_split
função principal :Divida a string
s
pelo resto dos argumentos, possivelmente omitindo partes vazias (empty
argumento de palavra-chave é o responsável por isso). Esta é uma função geradora.Quando apenas um delimitador é fornecido, a string é simplesmente dividida por ele.
empty
é entãoTrue
por padrão.Quando vários delimitadores são fornecidos, a sequência é dividida pelas sequências mais longas possíveis desses delimitadores por padrão ou, se
empty
for definido comoTrue
, sequências vazias entre os delimitadores também são incluídas. Observe que os delimitadores, neste caso, podem ser apenas caracteres únicos.Quando nenhum delimitador é fornecido,
string.whitespace
é usado, então o efeito é o mesmo questr.split()
, exceto que esta função é um gerador.Esta função funciona no Python 3 e uma correção fácil, embora bastante feia, pode ser aplicada para fazê-la funcionar nas versões 2 e 3. As primeiras linhas da função devem ser alteradas para:
fonte
Não, mas deve ser fácil escrever um usando
itertools.takewhile()
.EDITAR:
Implementação muito simples, meio quebrada:
fonte
takeWhile
. O que seria bompredicate
para dividir uma string em palavras (padrãosplit
) usandotakeWhile()
?string.whitespace
.'abc<def<>ghi<><>lmn'.split('<>') == ['abc<def', 'ghi', '', 'lmn']
Não vejo nenhum benefício óbvio em uma versão geradora desplit()
. O objeto gerador terá que conter a string inteira para iterar, então você não vai economizar memória por ter um gerador.Se você quisesse escrever um, seria bastante fácil:
fonte
id()
me colocar no lugar. E, obviamente, como as strings são imutáveis, você não precisa se preocupar com alguém alterando a string original enquanto você está iterando sobre ela.Eu escrevi uma versão da resposta de @ninjagecko que se comporta mais como string.split (ou seja, espaço em branco delimitado por padrão e você pode especificar um delimitador).
Aqui estão os testes que usei (em python 3 e python 2):
O módulo regex do python diz que ele faz "a coisa certa" para espaços em branco unicode, mas eu realmente não testei.
Também disponível como uma essência .
fonte
Se você também gostaria de ler um iterador (bem como retornar um), tente isto:
Uso
fonte
more_itertools.split_at
oferece um análogostr.split
para iteradores.more_itertools
é um pacote de terceiros.fonte
itertools.chain
e avaliar os resultados usando uma compreensão de lista. Dependendo da necessidade e solicitação, posso postar um exemplo.Eu queria mostrar como usar a solução find_iter para retornar um gerador para determinados delimitadores e, em seguida, usar a receita de pares de itertools para construir uma próxima iteração anterior que obterá as palavras reais como no método de divisão original.
Nota:
fonte
Método mais idiota, sem regex / itertools:
fonte
fonte
[f[j:i]]
e nãof[j:i]
?aqui está uma resposta simples
fonte