Repita a string para um determinado comprimento

204

Qual é uma maneira eficiente de repetir uma string com um determinado comprimento? Por exemplo:repeat('abc', 7) -> 'abcabca'

Aqui está meu código atual:

def repeat(string, length):
    cur, old = 1, string
    while len(string) < length:
        string += old[cur-1]
        cur = (cur+1)%len(old)
    return string

Existe uma maneira melhor (mais pitônica) de fazer isso? Talvez usando compreensão de lista?

John Howard
fonte

Respostas:

73
def repeat_to_length(string_to_expand, length):
   return (string_to_expand * ((length/len(string_to_expand))+1))[:length]

Para python3:

def repeat_to_length(string_to_expand, length):
    return (string_to_expand * (int(length/len(string_to_expand))+1))[:length]
Jason Scheirer
fonte
5
Parece que isso está aproveitando a divisão inteira. Isso não precisa estar //no Python 3? Ou largar +1e usar uma chamada explícita para uma função de teto seria suficiente. Além disso, uma observação: a sequência gerada realmente tem uma repetição extra quando se divide uniformemente; o extra é cortado pela emenda. Isso me confundiu no começo.
Jpmc26
int()faz a mesma coisa aqui, mas sim, //pode ser microscopicamente mais rápido, porque faz a divisão e o piso em um comando em vez de dois.
precisa saber é o seguinte
667

A resposta de Jason Scheirer está correta, mas poderia ser mais útil.

Primeiro, para repetir uma sequência um número inteiro de vezes, você pode usar a multiplicação sobrecarregada:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Portanto, para repetir uma sequência até que ela tenha pelo menos o comprimento desejado, calcule o número apropriado de repetições e coloque-o no lado direito desse operador de multiplicação:

def repeat_to_at_least_length(s, wanted):
    return s * (wanted//len(s) + 1)

>>> repeat_to_at_least_length('abc', 7)
'abcabcabc'

Em seguida, você pode apará-lo no comprimento exato desejado com uma fatia da matriz:

def repeat_to_length(s, wanted):
    return (s * (wanted//len(s) + 1))[:wanted]

>>> repeat_to_length('abc', 7)
'abcabca'

Como alternativa, conforme sugerido na resposta do pillmod, que provavelmente ninguém rola mais o suficiente para notar, você pode usar divmodpara calcular o número de repetições completas necessárias e o número de caracteres extras, tudo de uma vez:

def pillmod_repeat_to_length(s, wanted):
    a, b = divmod(wanted, len(s))
    return s * a + s[:b]

Qual é melhor? Vamos compará-lo:

>>> import timeit
>>> timeit.repeat('scheirer_repeat_to_length("abcdefg", 129)', globals=globals())
[0.3964178159367293, 0.32557755894958973, 0.32851039397064596]
>>> timeit.repeat('pillmod_repeat_to_length("abcdefg", 129)', globals=globals())
[0.5276265419088304, 0.46511475392617285, 0.46291469305288047]

Então, a versão do pillmod é algo 40% mais lento, o que é muito ruim, já que pessoalmente acho que é muito mais legível. Existem várias razões possíveis para isso, começando com a compilação de cerca de 40% mais instruções de bytecode.

Nota: esses exemplos usam o //operador new-ish para truncar a divisão inteira. Isso geralmente é chamado de recurso Python 3, mas, de acordo com o PEP 238 , foi introduzido no Python 2.2. Você só precisa usá-lo no Python 3 (ou nos módulos que o possuem from __future__ import division), mas pode usá-lo independentemente.

zwol
fonte
8
Não, o OP deseja que o resultado seja do tamanho 7 (que não é um múltiplo de 3).
IANS
1
Estou um pouco em conflito porque esta não é a resposta correta para OP, mas é a resposta correta para mim e 489 outras pessoas ...
Matt Fletcher
2
@MattFletcher Você acabou de me empurrou sobre a linha do "eu deveria reescrever isso como um explicador da resposta aceito" para "I vai reescrever isso ..." ;-)
Zwol
60

Isso é bastante pitônico:

newstring = 'abc'*5
print newstring[0:6]
Helen K
fonte
14
0:7se você quiser 7 caracteres como OP.
Mad Físico
34
def rep(s, m):
    a, b = divmod(m, len(s))
    return s * a + s[:b]
pillmuncher
fonte
14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))
kennytm
fonte
É isso que eu uso quando preciso iterar sobre a string (não é necessário ingressar). Deixe as bibliotecas python fazerem o trabalho.
Wihlke
7

Talvez não seja a solução mais eficiente, mas certamente curta e simples:

def repstr(string, length):
    return (string * length)[0:length]

repstr("foobar", 14)

Dá "foobarfoobarfo". Uma coisa sobre esta versão é que, se length <len (string), a string de saída será truncada. Por exemplo:

repstr("foobar", 3)

Dá "foo".

Edit: na verdade, para minha surpresa, isso é mais rápido que a solução atualmente aceita (a função 'repeat_to_length'), pelo menos em strings curtas:

from timeit import Timer
t1 = Timer("repstr('foofoo', 30)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo', 30)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~0.35 secs
t2.timeit()  # gives ~0.43 secs

Presumivelmente, se a corda fosse longa ou o comprimento fosse muito alto (isto é, se o desperdício da string * lengthpeça fosse alto), o desempenho seria fraco. E, de fato, podemos modificar o acima para verificar isso:

from timeit import Timer
t1 = Timer("repstr('foofoo' * 10, 3000)", 'from __main__ import repstr')
t2 = Timer("repeat_to_length('foofoo' * 10, 3000)", 'from __main__ import repeat_to_length')
t1.timeit()  # gives ~18.85 secs
t2.timeit()  # gives ~1.13 secs
Adam Parkin
fonte
1
Você pode adicionar uma alternância entre as duas versões com base nos comprimentos de entrada e saída para otimizar o máximo.
Mad Físico
6

E se string * (length / len(string)) + string[0:(length % len(string))]

murgatroid99
fonte
length / len(string)precisa ser invólucro entre parênteses, e você está perdendo o último ].
MikeWyatt
1
O mais legível / intuitivo até agora, na minha opinião. Eu acho que você precisa usar //para a divisão inteira no Python 3. A 0emenda é opcional. (O cólon é necessário, é claro.)
jpmc26
6

Eu uso isso:

def extend_string(s, l):
    return (s*l)[:l]
김민준
fonte
5

Não que não tenha havido respostas suficientes para essa pergunta, mas há uma função de repetição; só precisa fazer uma lista e juntar a saída:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
Amy Platt
fonte
Isso não responde à pergunta. Este repete a corda X vezes, não a repete até o comprimento X. Por exemplo "abc", 4, esperaria "abca". Isso criariaabcabcabcabc
Marcus Lind
3

Yay recursão!

def trunc(s,l):
    if l > 0:
        return s[:l] + trunc(s, l - len(s))
    return ''

Não será escalado para sempre, mas é bom para cordas menores. E é bonito.

Admito que acabei de ler o Little Schemer e gosto de recursão agora.

Tríptico
fonte
1

Essa é uma maneira de fazer isso usando uma compreensão de lista, embora seja cada vez mais inútil à medida que o comprimento da rptstring aumenta.

def repeat(rpt, length):
    return ''.join([rpt for x in range(0, (len(rpt) % length))])[:length]
vezult
fonte
0

Outra abordagem do FP:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
Aleš Kotnik
fonte
0
def extended_string (word, length) :

    extra_long_word = word * (length//len(word) + 1)
    required_string = extra_long_word[:length]
    return required_string

print(extended_string("abc", 7))
Aditya Verma
fonte