What is the best way to "periodically" replace characters in a string in Python?

I have a line where the character ('@') should be replaced with characters from the list of one or more characters "in order" and "periodically". So, for example, I have

'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z'

and want

'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z'

for replace_chars = ['1', '2', '3']

The problem is that in this example there is more @ in the line than I have substitutes for.

This is my attempt:

 result = '' replace_chars = ['1', '2', '3'] string = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z' i = 0 for char in string: if char == '@': result += replace_chars[i] i += 1 else: result += char print(result) 

but this only works, of course, if the source line has no more than three @, and otherwise I get an IndexError .

Edit: Thanks for the answers!

+8
python replace
source share
4 answers

Your code could be fixed by adding the line i = i%len(replace_chars) as the last line of your if clause. Thus, you will leave the remainder of dividing i by the length of your list of replacement characters.

A shorter solution is to use a generator that periodically splashes out replacement characters.

 >>> from itertools import cycle >>> s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z' >>> replace_chars = ['1', '2', '3'] >>> >>> replacer = cycle(replace_chars) >>> ''.join([next(replacer) if c == '@' else c for c in s]) 'ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z' 

For each character c in your string s we will get the next replacement character from the replacer generator if the character is '@' , otherwise it will just give you the original character.

For an explanation of why I used list comprehension instead of a generator expression, read this .

+10
source share

Generators are funny.

 def gen(): replace_chars = ['1', '2', '3'] while True: for rc in replace_chars: yield rc with gen() as g: s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z' s = ''.join(next(g) if c == '@' else c for c in s) 

As PM 2Ring suggested, this is functionally the same as itertools.cycle . The difference is that itertools.cycle will contain an additional copy of the list in memory, which may not be needed.

itertools.cycle source:

 def cycle(iterable): saved = [] for element in iterable: yield element saved.append(element) while saved: for element in saved: yield element 
+6
source share

You can also save your index logic after using modulo using the comp list using itertools.count to keep track of where you are:

 from itertools import count cn, ln = count(), len(replace_chars) print("".join([replace_chars[next(cn) % ln] if c == "@" else c for c in string])) ab1cde23fghi1jk2lmno312p3qrs1tuvwxy2z 
+1
source share

I think it’s better not to sort by character, especially for a long string with long parts without @ .

 from itertools import cycle, chain s = 'ab@cde@@fghi@jk@lmno@@@p@qrs@tuvwxy@z' replace_chars = ['1', '2', '3'] result = ''.join(chain.from_iterable(zip(s.split('@'), cycle(replace_chars))))[:-1] 

I do not know how to effectively kill the last char [:-1] .

0
source share

All Articles