Повторете низа до определена дължина

Какъв е ефективен начин за повторение на низ до определена дължина? Напр.: 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

Има ли по-добър (по-питоничен) начин да направите това? Може би с помощта на разбиране на списък?


person John Howard    schedule 02.08.2010    source източник


Отговори (11)


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]
person Jason Scheirer    schedule 02.08.2010
comment
Изглежда, че това се възползва от целочисленото деление. Това не трябва ли да бъде // в Python 3? Или изпускането на +1 и използването на изрично извикване на таванна функция би било достатъчно. Също така, бележка: генерираният низ всъщност има допълнително повторение, когато се разделя равномерно; допълнителното се отрязва от снаждането. Това ме обърка в началото. - person jpmc26; 04.05.2013
comment
int() прави същото нещо тук, но да, // може да е микроскопично по-бързо, тъй като прави разделяне и подаване в една команда вместо в две. - person Doyousketch2; 15.01.2018

Отговорът на Джейсън Шайрър е правилен, но би могъл да използва още малко изложение.

Първо, за да повторите низ цял брой пъти, можете да използвате претоварено умножение:

>>> '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), но можете да го използвате независимо от това.

person zwol    schedule 02.08.2010
comment
Не, OP иска резултатът да е с дължина 7 (което не е кратно на 3). - person IanS; 02.03.2017
comment
Малко съм в конфликт, защото това не е правилният отговор за OP, но е правилният отговор за мен и още 489 души... - person Matt Fletcher; 16.03.2018
comment
@MattFletcher Току-що ме подтикнахте към границата от Трябва да пренапиша това като обяснение на приетия отговор до ще пренапиша това... ;-) - person zwol; 16.03.2018
comment
Кодът на Pillmod / Pillmuncher е по-красив, но за съжаление по-бавен от кода на Scheirer, защото добавянето към вече дълъг низ е скъпа операция (времето за изпълнение на Python ще трябва да копира низа в ново място в паметта), докато съкращаването на временен низ е бързо . В допълнение, присвояването на променливи (и запазването на тяхното съхранение) е доста бавно в Python. - person Kai Petzke; 14.08.2020

Това е доста питонично:

newstring = 'abc'*5
print newstring[0:6]
person Helen K    schedule 12.11.2013
comment
0:7 ако искате 7 знака като OP. - person Mad Physicist; 21.06.2016

Може би не най-ефективното решение, но със сигурност кратко и просто:

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

repstr("foobar", 14)

Дава "foobarfoobarfo". Едно нещо за тази версия е, че ако дължина ‹ len(string), тогава изходният низ ще бъде съкратен. Например:

repstr("foobar", 3)

Дава "foo".

Редактиране: всъщност за моя изненада, това е по-бързо от текущо приетото решение (функцията '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
person Adam Parkin    schedule 26.01.2012
comment
Можете да добавите превключване между двете версии въз основа на входната и изходната дължина за максимална оптимизация. - person Mad Physicist; 21.06.2016
comment
@MadPhysicist Това превключване между двете версии вероятно ще добави приблизително толкова режийни разходи, колкото струва разделението в отговора на Джейсън Шайрър. - person Kai Petzke; 14.08.2020

Какво ще кажете за string * (length / len(string)) + string[0:(length % len(string))]

person murgatroid99    schedule 02.08.2010
comment
length / len(string) трябва да бъде обвивка в скоби и ви липсва последното ]. - person MikeWyatt; 02.08.2010
comment
Най-четливият/интуитивен досега според мен. Мисля, че трябва да използвате // за целочислено деление в Python 3. 0 в снаждането не е задължително. (Двоеточие е задължително, разбира се.) - person jpmc26; 04.05.2013

използвам това:

def extend_string(s, l):
    return (s*l)[:l]
person 김민준    schedule 12.09.2013

Не че няма достатъчно отговори на този въпрос, но има функция за повторение; просто трябва да направите списък и след това да се присъедините към изхода:

from itertools import repeat

def rep(s,n):
  ''.join(list(repeat(s,n))
person Amy Platt    schedule 20.09.2012
comment
Това не отговаря на въпроса. Този повтаря низа X пъти, не го повтаря до X дължина. напр. "abc", 4 би очаквал "abca". Това би създало abcabcabcabc - person Marcus Lind; 09.04.2019

Ура рекурсия!

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

Няма да се мащабира вечно, но е добре за по-малки низове. И е хубава.

Признавам, че току-що прочетох Little Schemer и харесвам рекурсията в момента.

person Triptych    schedule 02.08.2010

Това е един от начините да го направите с помощта на разбиране на списък, въпреки че е все по-разточителен, тъй като дължината на низа rpt се увеличава.

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

Друг FP подход:

def repeat_string(string_to_repeat, repetitions):
    return ''.join([ string_to_repeat for n in range(repetitions)])
person Aleš Kotnik    schedule 19.02.2012

person    schedule
comment
Това е, което използвам, когато трябва само да повторя низа (тогава не е необходимо присъединяване). Оставете библиотеките на Python да свършат работата. - person wihlke; 06.05.2018