Come trasformare% s in {0}, {1} ... meno goffo?


11

Devo prendere una stringa contenente segnaposto per la sostituzione successiva, come:

"A %s B %s"

E trasformalo in:

"A {0} B {1}"

Mi è venuta in mente:

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

Funziona, ma sembra un python super goffo e per niente "idiomatico".

Come sarebbe una soluzione idiomatica di pitone?

Aggiornamenti per chiarimenti:

  • le stringhe risultanti non vengono utilizzate in Python. Io faccio bisogno dei numeri del contatore in là! (quindi {}non è abbastanza buono)!
  • Ho solo bisogno di preoccuparmi delle %sstringhe, dato che i messaggi sono sicuri di usare solo %s(no %i %f)

"re.sub" può assumere una funzione di sostituzione per sostituire dinamicamente con parentesi graffe numerate. A proposito: anche la sostituzione con {} senza numeri funzionerebbe.
Michael Butscher,

3
Quando hai una soluzione funzionante e desideri migliorarla, ti preghiamo di considerare codereview.stackexchange.com
kojiro

@kojiro Non funziona per me, a causa di "troppo goffo" ;-)
GhostCat saluta Monica C.

Risposte:


5

Vorrei fare ciò che Reznik inizialmente suggeriva e poi invitare .formata questo:

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

questo funzionerà solo se ne hai 2 %snel testo?
Charif DZ,

@CharifDZ e Aran-Fey - guarda la modifica. Li conti appena prima ...
Dan

Sembra un buon compromesso ... leggibile, ma non troppo elegante.
GhostCat saluta Monica C. il

8

Utilizzare re.subcon una funzione lambda per riapplicare la sostituzione una volta per ciascun elemento e itertools.countper ottenere i numeri in sequenza:

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

Ricorda di avvolgerlo in una funzione per eseguire questa operazione più di una volta, poiché dovrai aggiornare itertools.count.


Bello, anche se lo trovo un po '"oscuro" ;-)
GhostCat saluta Monica C.

@GhostCat non è oscuro, il contatore è un generatore quando mai lo chiami next(counter)produce il valore successivo, mi dimentico sempre di questa risposta davvero bella generatore
Charif DZ

@GhostCat regex e programmazione funzionale nella stessa riga: -PI indovina "l'oscurità" dipende da quanto sei abituato a questi strumenti.
jfaccioni,

3

Penso che dovrebbe funzionare

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


1
@GhostCat non lasciarti tentare da una riga, ricorda che le persone devono mantenere il codice
Dan

1
@Dan lo so. Ma meno codice è anche meno codice da mantenere.
GhostCat saluta Monica C. il

Non è meno però, è solo più difficile eseguire il debug e più difficile da leggere per un futuro sviluppatore. Questa risposta è abbastanza vicina all'essere identica alla mia, ha le stesse chiamate di funzione (sostituzione dello scambio con split e join) ma cosa succede se c'è un problema in una fase intermedia? Come lo isoleresti. E che è più veloce di nuovi sviluppatori per capire cosa fa solo dal codice. Consiglio vivamente di non mettere tutta questa logica in una sola riga.
Dan

1
Non è una bella fodera. X.join(Y.split(Z))è solo un modo contorto di scrivere Y.replace(X, Z), e non è nemmeno necessario racchiudere la rangechiamata list(...).
Aran-Fey,

1
@Dan sì, scusami, (solo in una riga ora) :)
Reznik

1

Usando re.subper la sostituzione dinamica:

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
Tieni presente che questo non tiene conto dei segnaposto di escape ( %%s) - se è un problema, puoi utilizzare r'(?<!%)%s'invece regex .
Aran-Fey,

Se permetti la domanda stupida: di cosa {{{}}}parlano in realtà?
GhostCat saluta Monica C. il

Formare le stringhe per fuggire {non facciamo \{come al solito {{.
Charif DZ,

1

Utilizzando un generatore:

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

Hai provato con .format?

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

Così:

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


1
Come funziona questo aiuto a nessuno girare %sin {}?
Aran-Fey,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.