Bagaimana mengubah% s menjadi {0}, {1} ... kurang kikuk?

11

Saya harus mengambil string yang berisi placeholder untuk substitusi nanti, seperti:

"A %s B %s"

Dan mengubahnya menjadi:

"A {0} B {1}"

Saya datang dengan:

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

Itu bekerja, tetapi rasanya super kikuk, dan sama sekali tidak python "idiomatik".

Bagaimana solusi python idiomatik yang baik?

Pembaruan untuk klarifikasi:

  • string yang dihasilkan tidak digunakan dalam python. Saya lakukan perlu nomor meja di sana! (jadi {}tidak cukup baik)!
  • Saya hanya perlu peduli dengan %sstring, karena pesan dijamin hanya akan digunakan %s(tidak %i %fapa pun)
GhostCat memberi hormat Monica C.
sumber
"re.sub" dapat berfungsi sebagai pengganti untuk mengganti secara dinamis dengan kawat gigi bernomor. Ngomong-ngomong: Mengganti dengan {} tanpa angka juga akan berhasil.
Michael Butscher
3
Ketika Anda memiliki solusi yang berfungsi dan Anda ingin memperbaikinya, silakan pertimbangkan codereview.stackexchange.com
kojiro
@kojiro Ini tidak berfungsi untuk saya, karena "terlalu kikuk" ;-)
GhostCat memberi hormat kepada Monica C.

Jawaban:

5

Saya akan melakukan apa yang awalnya disarankan Reznik dan kemudian memanggil .formatitu:

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
sumber
ini hanya akan berfungsi jika Anda memiliki 2 %sdi teks?
Charif DZ
@CharifDZ dan Aran-Fey - lihat hasil edit. Anda hanya menghitungnya sebelum tangan ...
Dan
Sepertinya kompromi yang bagus ... mudah dibaca, tetapi tidak terlalu mewah.
GhostCat memberi hormat Monica C.
8

Gunakan re.subdengan fungsi lambda untuk menerapkan kembali substitusi satu kali untuk setiap elemen, dan itertools.countuntuk mendapatkan angka secara berurutan:

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

Jangan lupa untuk membungkusnya dalam fungsi untuk melakukan operasi ini lebih dari sekali, karena Anda harus menyegarkan itertools.count.

jfaccioni
sumber
Bagus, meskipun saya merasa sedikit "tidak jelas" ;-)
GhostCat memberi hormat kepada Monica C.
@ GhostCat itu tidak jelas, penghitung adalah generator ketika Anda menyebutnya next(counter)menghasilkan nilai berikutnya, saya selalu lupa tentang generator ini jawaban yang sangat bagus
Charif DZ
@GhostCat regex dan pemrograman fungsional di baris yang sama: -PI kira "ketidakjelasan" tergantung pada seberapa banyak Anda terbiasa dengan alat-alat ini.
jfaccioni
3

Saya pikir itu harus bekerja

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

Reznik
sumber
1
@GhostCat jangan tergoda oleh one-liners, ingat bahwa orang harus menjaga kode
Dan
1
@Dan saya tahu. Tapi kode saya kurang juga kurang kode untuk mempertahankan.
GhostCat memberi hormat Monica C.
Ini tidak kurang, hanya lebih sulit untuk debug dan lebih sulit untuk pengembang masa depan untuk membaca. Jawaban ini cukup dekat untuk menjadi identik dengan saya, ia memiliki panggilan fungsi yang sama (bertukar ganti untuk split dan bergabung) tetapi bagaimana jika ada masalah dalam langkah menengah? Bagaimana Anda mengisolasinya? Dan mana yang lebih cepat dari pengembang baru untuk memahami apa yang dilakukannya hanya dari kode. Saya sangat merekomendasikan Anda untuk tidak menempatkan logika sebanyak ini dalam satu baris.
Dan
1
Bukan liner yang bagus. X.join(Y.split(Z))hanya cara penulisan yang berbelit-belit Y.replace(X, Z), dan tidak perlu membungkus rangepanggilan itu list(...)juga.
Aran-Fey
1
@Dan ya, maaf soal itu, (hanya dalam satu baris sekarang) :)
Reznik
1

Menggunakan re.subuntuk mengganti dinamis:

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
sumber
1
Ingatlah bahwa ini tidak termasuk placeholder yang lolos ( %%s) - jika itu masalah, Anda bisa menggunakan regex r'(?<!%)%s'sebagai gantinya.
Aran-Fey
Jika Anda membiarkan pertanyaan bodoh: apa yang {{{}}}sebenarnya terjadi?
GhostCat memberi hormat Monica C.
String formating untuk melarikan diri {tidak kita lakukan \{seperti biasa {{.
Charif DZ
1

Menggunakan generator:

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
sumber
-4

Sudahkah Anda mencoba dengan .format?

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

Begitu:

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

Alfonso
sumber
1
Bagaimana ini membantu siapa pun berubah %smenjadi {}?
Aran-Fey