Comment transformer% s en {0}, {1}… moins maladroit?


11

Je dois prendre une chaîne contenant des espaces réservés pour une substitution ultérieure, comme:

"A %s B %s"

Et transformez cela en:

"A {0} B {1}"

Je suis venu avec:

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

Cela fonctionne, mais il se sent super maladroit, et pas du tout python "idiomatique".

À quoi ressemblerait une solution de python idiomatique?

Mises à jour pour clarification:

  • les chaînes résultantes ne sont pas utilisées dans python. Je fais besoin des numéros de compteur là - dedans! (donc ça {}ne suffit pas)!
  • J'ai seulement besoin de me soucier des %schaînes, car les messages sont garantis pour n'utiliser que %s(pas du %i %ftout)

"re.sub" peut prendre une fonction de remplacement pour remplacer dynamiquement par des accolades numérotées. Soit dit en passant: le remplacement par {} sans chiffres fonctionnerait également.
Michael Butscher

3
Lorsque vous avez une solution de travail et que vous souhaitez l'améliorer, veuillez considérer codereview.stackexchange.com
kojiro

@kojiro Cela ne fonctionne pas pour moi, en raison de "trop ​​maladroit" ;-)
GhostCat salue Monica C.

Réponses:


5

Je ferais ce que Reznik a suggéré à l' origine , puis j'appellerais .formatcela:

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

cela ne fonctionnera que si vous en avez 2 %sdans le texte?
Charif DZ

@CharifDZ et Aran-Fey - voir l'édition. Vous les comptez juste avant la main ...
Dan

On dirait un bon compromis ... lisible, mais pas trop chic.
GhostCat salue Monica C.

8

À utiliser re.subavec une fonction lambda pour réappliquer la substitution une fois pour chaque élément et itertools.countpour obtenir des nombres séquentiellement:

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

N'oubliez pas de mettre cela dans une fonction pour effectuer cette opération plusieurs fois, car vous devrez actualiser itertools.count.


Sympa, bien que je trouve ça un peu "obscur" ;-)
GhostCat salue Monica C.

@GhostCat ce n'est pas obscur, le compteur est un générateur lorsque vous l'appelez next(counter), produisez la valeur suivante, j'oublie toujours ce générateur vraiment une bonne réponse
Charif DZ

@GhostCat regex et programmation fonctionnelle dans la même ligne: -PI suppose que "l'obscurité" dépend de combien vous êtes habitué à ces outils.
jfaccioni

3

Je pense que ça devrait marcher

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


1
@GhostCat ne vous laissez pas tenter par les one-liners, rappelez-vous que les gens doivent maintenir le code
Dan

1
@Dan je sais. Mais j'ai moins de code, c'est aussi moins de code à maintenir.
GhostCat salue Monica C.

Ce n'est pas moins cependant, c'est juste plus difficile à déboguer et plus difficile à lire pour un futur développeur. Cette réponse est assez proche de la mienne, elle a les mêmes appels de fonction (échange de remplacement pour split et join) mais qu'en est-il s'il y a un problème dans une étape intermédiaire? Comment pourriez-vous l'isoler. Et qui est plus rapide pour un nouveau développeur de comprendre ce qu'il fait uniquement à partir du code. Je vous recommande vraiment de ne pas mettre autant de logique sur une seule ligne.
Dan

1
Pas une belle ligne. X.join(Y.split(Z))est juste une façon compliquée d'écrire Y.replace(X, Z), et il n'est pas nécessaire non plus de terminer l' rangeappel list(...).
Aran-Fey

1
@Dan oui, désolé, (en une seule ligne maintenant) :)
Reznik

1

Utilisation re.subpour le remplacement dynamique:

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}

1
Gardez à l'esprit que cela ne tient pas compte des espaces réservés échappés ( %%s) - si c'est un problème, vous pouvez utiliser l'expression régulière à la r'(?<!%)%s'place.
Aran-Fey

Si vous autorisez la question stupide: de quoi s'agit-il {{{}}}réellement?
GhostCat salue Monica C.

La formation de chaînes pour {nous échapper ne fait pas \{comme d'habitude {{.
Charif DZ

1

À l'aide d'un générateur:

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

Avez-vous essayé avec .format?

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

Donc:

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


1
Comment cette aide quelqu'un ne se transforme %sen {}?
Aran-Fey
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.