Простой способ с re.subn , который также может принимать функцию вместо строки замены:
import re
from random import randint
def select(m):
choices = m.group(1).split('|')
return choices[randint(0, len(choices)-1)]
def spinner(s):
r = re.compile('{([^{}]*)}')
while True:
s, n = r.subn(select, s)
if n == 0: break
return s.strip()
Он просто заменяет все самые глубокие варианты, которые он встречает, а затем повторяется до тех пор, пока не останется выбора. subn
возвращает кортеж с результатом и количеством выполненных замен, что удобно для определения окончания обработки.
Моя версия select()
может быть заменена на версию Bobince, которая использует random.choice()
и более элегантна, если вы просто хотите использовать случайный селектор. Если вы хотите построить дерево выбора, вы можете расширить вышеупомянутую функцию, но вам понадобятся глобальные переменные, чтобы отслеживать, где вы находитесь, поэтому перемещение функций в класс имело бы смысл. Это всего лишь подсказка, я не буду развивать эту идею, так как это не был оригинальный вопрос.
Обратите внимание, наконец, что вы должны использовать r.subn(select, s, re.U)
, если вам нужны строки Unicode (s = u"{...}"
)
Пример:
>>> s = "{{Hello|Hi|Hey} {world|earth} | {Goodbye|farewell} {noobs|n3wbz|n00blets}}"
>>> print spinner(s)
'farewell n3wbz'
Редактировать: Заменить sub
на subn
, чтобы избежать бесконечного цикла (спасибо Bobince за это) и сделать его более эффективным, и заменить {([^{}]+)}
на {([^{}]*)}
, чтобы извлечь пустой фигурные скобки также. Это должно сделать его более устойчивым к плохо отформатированным шаблонам.
Для людей, которые любят ставить как можно больше на одну строку (что я лично не рекомендовал бы):
def spin(s):
while True:
s, n = re.subn('{([^{}]*)}',
lambda m: random.choice(m.group(1).split("|")),
s)
if n == 0: break
return s.strip()