Wie verwandle ich% s in {0}, {1}… weniger klobig?

11

Ich muss eine Zeichenfolge mit Platzhaltern für eine spätere Ersetzung verwenden, z.

"A %s B %s"

Und machen Sie daraus:

"A {0} B {1}"

Ich hatte die Idee dass:

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

Das funktioniert, aber es fühlt sich super klobig an und überhaupt nicht "idiomatisch" Python.

Wie würde eine gute, idiomatische Python-Lösung aussehen?

Aktualisierungen zur Klarstellung:

  • Die resultierenden Zeichenfolgen werden in Python nicht verwendet. Ich tun die Zählernummern dort brauchen! (also {}nicht gut genug)!
  • Ich muss mich nur um %sStrings kümmern , da die Nachrichten garantiert nur verwendet werden %s( %i %füberhaupt nicht).
GhostCat begrüßt Monica C.
quelle
"re" kann eine Funktion als Ersatz übernehmen, um dynamisch durch nummerierte Klammern zu ersetzen. Übrigens: Das Ersetzen durch {} ohne Zahlen würde ebenfalls funktionieren.
Michael Butscher
3
Wenn Sie eine funktionierende Lösung haben und diese verbessern möchten, ziehen Sie bitte
codereview.stackexchange.com
@kojiro Es funktioniert nicht für mich, weil "zu klobig" ;-)
GhostCat begrüßt Monica C.

Antworten:

5

Ich würde tun, was Reznik ursprünglich vorgeschlagen hatte, und dann darauf zurückgreifen .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
Dan
quelle
Dies wird nur funktionieren, wenn Sie 2 %sim Text haben?
Charif DZ
@CharifDZ und Aran-Fey - siehe die Bearbeitung. Sie zählen sie nur vor der Hand ...
Dan
Sieht nach einem guten Kompromiss aus ... lesbar, aber nicht zu ausgefallen.
GhostCat begrüßt Monica C.
8

Verwendung re.submit einer Lambda-Funktion zum erneuten Anwenden der Substitution für jedes Element und itertools.countzum sequentiellen Abrufen von Zahlen:

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

Denken Sie daran, dies in eine Funktion zu verpacken, um diesen Vorgang mehrmals auszuführen, da Sie eine Aktualisierung durchführen müssen itertools.count.

jfaccioni
quelle
Schön, obwohl ich es etwas "dunkel"
finde ;-)
@ GhostCat es ist nicht dunkel, Zähler ist ein Generator, wann immer Sie es nennen next(counter), produzieren Sie den nächsten Wert, ich vergesse immer über diesen Generator wirklich schöne Antwort
Charif DZ
@ GhostCat Regex und funktionale Programmierung in derselben Zeile: -PI Ich denke, die "Dunkelheit" hängt davon ab, wie sehr Sie an diese Tools gewöhnt sind.
jfaccioni
3

Ich denke, das sollte funktionieren

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

Reznik
quelle
1
@ GhostCat nicht von Einzeilern versucht werden, denken Sie daran, dass die Leute Code pflegen müssen
Dan
1
@ Dan ich weiß. Aber ich weniger Code ist auch weniger Code zu pflegen.
GhostCat begrüßt Monica C.
Es ist jedoch nicht weniger, es ist nur schwieriger zu debuggen und für einen zukünftigen Entwickler schwieriger zu lesen. Diese Antwort ist fast identisch mit meiner, sie hat dieselben Funktionsaufrufe (Austausch von Ersetzen gegen Teilen und Verbinden), aber was ist, wenn in einem Zwischenschritt ein Problem auftritt? Wie würden Sie es isolieren? Und was ist schneller von einem neuen Entwickler zu verstehen, was es nur aus dem Code macht. Ich empfehle Ihnen wirklich, nicht so viel Logik in eine einzelne Zeile zu setzen.
Dan
1
Kein schöner Einzeiler. X.join(Y.split(Z))ist nur eine verschlungene Schreibweise Y.replace(X, Z), und es besteht auch keine Notwendigkeit, den rangeAnruf list(...)einzuschließen.
Aran-Fey
1
@ Dan ja, tut mir leid, (jetzt nur in einer Zeile) :)
Reznik
1

Verwenden re.subfür dynamisches Ersetzen:

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
quelle
1
Beachten Sie, dass dies keine maskierten Platzhalter berücksichtigt ( %%s). Wenn dies ein Problem darstellt, können Sie r'(?<!%)%s'stattdessen den regulären Ausdruck verwenden.
Aran-Fey
Wenn Sie die dumme Frage zulassen: Worum geht es {{{}}}eigentlich?
GhostCat begrüßt Monica C.
String-Formatierung, um zu entkommen, machen {wir nicht \{wie gewöhnlich {{.
Charif DZ
1

Verwendung eines Generators:

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"))
Pulsar
quelle
-4

Haben Sie es mit .format versucht?

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

Damit:

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

Alfonso
quelle
1
Wie wird aus dieser Hilfe jemand %sin {}?
Aran-Fey