Как превратить% s в {0}, {1} ... менее неуклюже?

11

Я должен взять строку, содержащую заполнители для последующей замены, например:

"A %s B %s"

И превратить это в:

"A {0} B {1}"

Я придумал:

def _fix_substitution_parms(raw_message):
  rv = raw_message
  counter = 0
  while '%s' in rv:
    rv = rv.replace('%s', '{' + str(counter) + '}', 1)
    counter = counter + 1
return rv

Это работает, но это кажется очень неуклюжим, и вовсе не «идиоматическим» питоном.

Как бы хорошо, идиоматическое решение Python выглядело?

Обновления для уточнения:

  • Результирующие строки не используются в Python. Я делать нужно число счетчиков в там! (так {}не достаточно хорошо)!
  • Мне нужно заботиться только о %sстроках, так как сообщения гарантированно использовать только %s(ни в коем случае %i %f)
GhostCat приветствует Монику С.
источник
«re.sub» может принимать функцию в качестве замены, чтобы динамически заменять пронумерованными скобками. Кстати: замена на {} без номеров тоже подойдет.
Майкл Бучер
3
Если у вас есть работающее решение, и вы хотите его улучшить, рассмотрите codereview.stackexchange.com
kojiro
@kojiro Это не работает для меня из-за "слишком неуклюжего" ;-)
GhostCat приветствует Монику С.

Ответы:

5

Я бы сделал то, что изначально предлагал Резник, а затем позвонил бы по этому поводу .format:

def _fix_substitution_parms(raw_message: str) -> str:
    num_to_replace = raw_message.count("%s")
    python_format_string_message = raw_message.replace("%s", "{{{}}}")
    final_message = python_format_string_message.format(*range(num_to_replace))
    return final_message
Дэн
источник
это будет работать, только если у вас есть 2 %sв тексте?
Charif DZ
@CharifDZ и Aran-Fey - см. Редактирование. Вы просто посчитаете их перед рукой ...
Дан
Похоже, хороший компромисс ... читабельный, но не слишком причудливый.
GhostCat приветствует Монику С.
8

Используйте re.subс лямбда-функцией для повторного применения подстановки один раз для каждого элемента и itertools.countдля последовательного получения чисел:

import itertools
import re

s = "A %s B %s"

counter = itertools.count()
result = re.sub('%s', lambda x: f'{{{next(counter)}}}', s)
print(result)  # 'A {0} B {1}'

Не забудьте заключить это в функцию, чтобы выполнить эту операцию более одного раза, так как вам нужно будет обновить itertools.count.

jfaccioni
источник
Хорошо, хотя я нахожу это немного "неясным" ;-)
GhostCat приветствует Монику С.
@GhostCat это не скрыто, счетчик - это генератор, когда вы next(counter)его называете, выдает следующее значение, я всегда забываю об этом генераторе - действительно хороший ответ
Чариф Д.З.
@ GhostCat регулярное выражение и функциональное программирование в одной строке: -Похожу, «неизвестность» зависит от того, насколько вы привыкли к этим инструментам.
jfaccioni
3

Я думаю, что работа должна

rv.replace('%s','{{{}}}').format(*range(rv.count('%s')))

Резник
источник
1
@GhostCat не поддавайтесь соблазнам, помните, что люди должны поддерживать код
Дан
1
@ Я знаю. Но я меньше кода и меньше кода для поддержки.
GhostCat приветствует Монику С.
Это не меньше, просто сложнее отлаживать и труднее читать будущему разработчику. Этот ответ очень похож на мой, он имеет те же самые вызовы функций (замена заменяет на split и join), но что, если на промежуточном этапе возникает проблема? Как бы вы изолировали это. И что быстрее нового разработчика, чтобы понять, что он делает только из кода. Я действительно рекомендую вам не помещать столько логики в одну строку.
Дан
1
Не хороший лайнер. X.join(Y.split(Z))это просто запутанный способ написания Y.replace(X, Z), и нет необходимости заключать rangeвызов list(...).
Аран-Фей
1
@ Дан, да, извините за это, (только в одной строке) :)
Резник
1

Использование re.subдля динамической замены:

import re

text = "A %s B %s %s B %s"


def _fix_substitution_parms(raw_message):
    counter = 0
    def replace(_):
        nonlocal counter
        counter += 1
        return '{{{}}}'.format(counter - 1)
    return re.sub('%s', replace, raw_message)


print(_fix_substitution_parms(text))  # A {0} B {1} {2} B {3}
Charif DZ
источник
1
Имейте в виду, что это не учитывает экранированные заполнители ( %%s) - если это проблема, вы можете r'(?<!%)%s'вместо этого использовать регулярное выражение .
Аран-Фей
Если вы позволите глупый вопрос: о чем на {{{}}}самом деле?
GhostCat приветствует Монику С.
Формирование строки, чтобы убежать, {мы не делаем \{как обычно, мы делаем {{.
Charif DZ
1

Используя генератор:

def split_and_insert(mystring):
    parts = iter(mystring.split('%s'))
    yield next(parts)
    for n, part in enumerate(parts):
        yield f'{{{n}}}'
        yield part

new_string = ''.join(split_and_insert("A %s B %s"))
пульсар
источник
-4

Вы пробовали с .format?

string.format (0 = значение, 1 = значение)

Так:

"A {0} B {1}".format(0=value, 1=value)

Alfonso
источник
1
Как это помогает кому-либо превратиться %sв {}?
Аран-Фей