Como transformar% s em {0}, {1} ... menos desajeitado?


11

Eu tenho que pegar uma string contendo espaços reservados para substituição posterior, como:

"A %s B %s"

E transforme isso em:

"A {0} B {1}"

Eu vim com:

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

Isso funciona, mas parece super desajeitado, e nem um pouco python "idiomático".

Como seria uma solução python bem idiomática?

Atualizações para esclarecimentos:

  • as seqüências resultantes não são usadas no python. I fazer precisar os números do contador em lá! (então {}não é bom o suficiente)!
  • Eu só preciso me preocupar com %sseqüências de caracteres, pois as mensagens são garantidas para usar apenas %s(não é o %i %fque for)

"re.sub" pode assumir uma função como substituição para substituir dinamicamente por chaves numeradas. A propósito: Substituir por {} sem números também funcionaria.
Michael Butscher 14/10/19

3
Quando você tem uma solução de trabalho e você gostaria de melhorá-lo, por favor considere codereview.stackexchange.com
kojiro

@kojiro Não está funcionando para mim, devido a "muito desajeitado" ;-)
GhostCat saúda Monica C.

Respostas:


5

Eu faria o que Reznik originalmente sugeriu e depois apelou .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

isso funcionará apenas se você tiver 2 %sno texto?
Charif DZ

@CharifDZ e Aran-Fey - veja a edição. Você apenas contá-las antes da mão ...
Dan

Parece um bom compromisso ... legível, mas não muito chique.
GhostCat saúda Monica C.

8

Use re.subcom uma função lambda para reaplicar a substituição uma vez para cada elemento e itertools.countpara obter números sequencialmente:

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}'

Lembre-se de agrupar isso em uma função para executar esta operação mais de uma vez, pois você precisará atualizar itertools.count.


Bom, embora eu encontrá-lo um pouco "obscuro" ;-)
GhostCat saudações Monica C.

@GhostCat não é obscuro, contador é um gerador sempre que você chamá next(counter)-lo produzir o próximo valor, eu sempre esquecer este gerador realmente resposta agradável
Charif DZ

Regex @GhostCat e programação funcional na mesma linha: -PI acho que a "obscuridade" depende de quanto você está acostumado a essas ferramentas.
Jfaccioni # 14/19

3

Eu acho que esse trabalho deve

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


11
@GhostCat não ser tentado pelo one-liners, lembre-se que as pessoas têm para manter o código
Dan

11
@ Dan eu sei. Mas menos código também é menos código para manter.
GhostCat saúda Monica C.

Não é menos, é apenas mais difícil depurar e mais difícil para um desenvolvedor futuro ler. Essa resposta está bem próxima de ser idêntica à minha, tem as mesmas chamadas de função (troca de substituição por divisão e junção), mas e se houver um problema em uma etapa intermediária? Como você o isolaria. E o que é mais rápido para o novo desenvolvedor entender o que ele faz apenas com o código. Eu realmente recomendo que você não coloque tanta lógica em uma única linha.
Dan

11
Não é uma frase legal. X.join(Y.split(Z))é apenas uma maneira complicada de escrever Y.replace(X, Z)e não há necessidade de encerrar a rangeligação list(...).
21419 Aran-Fey

11
@ Dan sim, sinto muito por isso, (apenas em uma linha agora) :)
Reznik

1

Usando re.subpara substituição dinâmica:

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}

11
Lembre-se de que isso não leva em consideração os espaços reservados vazados ( %%s). Se isso for um problema, você pode usar a regex r'(?<!%)%s'.
22819 Aran-Fey

Se você permitir a pergunta estúpida: sobre o que {{{}}}realmente é?
GhostCat saúda Monica C. 15/10/19

Formatação de string para escapar {, não fazemos \{como de costume {{.
Charif DZ

1

Usando um gerador:

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

Você já tentou com .format?

string.format (0 = valor, 1 = valor)

Assim:

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


11
Como isso ajuda qualquer um transformar %sem {}?
22819 Aran-Fey
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.