Повторите строку до определенной длины


205

Как эффективно повторить строку определенной длины? Например:repeat('abc', 7) -> 'abcabca'

Вот мой текущий код:

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

Есть ли лучший (более питонический) способ сделать это? Может быть, используя понимание списка?

Ответы:


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

Для python3:

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

5
Похоже, это использует целочисленное деление. Разве это не должно быть //в Python 3? Или +1было бы достаточно отбросить и использовать явный вызов функции потолка. Кроме того, примечание: сгенерированная строка на самом деле имеет дополнительное повторение, когда она делится равномерно; лишнее отрезано соединением. Это смутило меня сначала.
jpmc26

int()делает то же самое здесь, но, да, //может быть микроскопически быстрее, потому что он делит и пол в одной команде вместо двух.
Doyousketch2

667

Ответ Джейсона Шайрера верен, но мог бы использовать еще немного изложения.

Прежде всего, чтобы повторить строку целое число раз, вы можете использовать перегруженное умножение:

>>> 'abc' * 7
'abcabcabcabcabcabcabc'

Итак, чтобы повторять строку до тех пор, пока она не будет хотя бы той длины, которую вы хотите, вы вычисляете соответствующее количество повторений и помещаете ее в правую часть этого оператора умножения:

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

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

Затем вы можете обрезать его до нужной длины с помощью среза массива:

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

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

В качестве альтернативы, как указано в ответе pillmod, что, вероятно, никто не прокручивает достаточно далеко, чтобы заметить больше, вы можете использовать, divmodчтобы вычислить количество необходимых полных повторений и количество дополнительных символов, все сразу:

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

Что лучше? Давайте оценим это:

>>> 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]

Итак, версия pillmod работает примерно на 40% медленнее, что очень плохо, так как лично я думаю, что она гораздо более читабельна. Для этого есть несколько возможных причин, начиная с его компиляции примерно на 40% больше инструкций байт-кода.

Примечание: в этих примерах используется //оператор new-ish для усечения целочисленного деления. Это часто называют функцией Python 3, но, согласно PEP 238 , она была введена еще в Python 2.2. Вы должны использовать его только в Python 3 (или в модулях, которые имеют from __future__ import division), но вы можете использовать его независимо.


8
Нет, OP хочет, чтобы результат был длиной 7 (которая не кратна 3).
Янв

1
Я немного противоречив, потому что это не правильный ответ для ОП, но это правильный ответ для меня и 489 других людей ...
Мэтт Флетчер

2
@MattFletcher Вы только что подтолкнули меня за строчку из «Я должен переписать это как объяснитель принятого ответа» на «Я буду переписать ...» ;-)
zwol



14
from itertools import cycle, islice
def srepeat(string, n):
   return ''.join(islice(cycle(string), n))

Это то, что я использую, когда мне нужно только перебрать строку (тогда соединение не требуется). Пусть библиотеки Python сделают свою работу.
wihlke

7

Возможно, не самое эффективное решение, но, конечно, короткое и простое:

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

repstr("foobar", 14)

Дает "foobarfoobarfo". Одна вещь в этой версии заключается в том, что если длина <len (строка), то выходная строка будет усечена. Например:

repstr("foobar", 3)

Дает "фу".

Edit: на самом деле, к моему удивлению, это быстрее, чем текущее принятое решение (функция repeat_to_length), по крайней мере, для коротких строк:

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

Предположительно, если нить была длинной или длина была очень высокой (то есть, если расточительность string * lengthдетали была высокой), то она работала бы плохо. И на самом деле мы можем изменить вышеупомянутое, чтобы проверить это:

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

1
Вы можете добавить переключение между двумя версиями на основе длины ввода и вывода для максимальной оптимизации.
Безумный физик

6

Как насчет string * (length / len(string)) + string[0:(length % len(string))]


length / len(string)нужно заключить в скобки обертку, а вам не хватает последнего ].
MikeWyatt

1
Самый читаемый / интуитивно понятный, на мой взгляд. Я думаю, что вам нужно использовать //для целочисленного деления в Python 3. В 0соединении не является обязательным. (Двоеточие обязательно, конечно.)
jpmc26


5

Не то чтобы не было достаточно ответов на этот вопрос, но есть функция повтора; просто нужно составить список и затем присоединиться к выводу:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))

Это не отвечает на вопрос. Этот повтор строки X раз, он не повторяется до X длины. Например, "abc", 4можно ожидать "abca". Это создастabcabcabcabc
Маркус Линд

3

Уу рекурсия!

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

Не будет масштабироваться вечно, но это хорошо для небольших струн. И это красиво.

Я признаю, что только что прочитал Маленького Схемера, и мне нравится рекурсия прямо сейчас.


1

Это один из способов сделать это с помощью понимания списка, хотя он становится все более расточительным по мере увеличения длины rptстроки.

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


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))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.