Apakah mungkin untuk melakukan pemformatan string parsial dengan metode pemformatan string lanjutan, mirip dengan safe_substitute()
fungsi template string ?
Sebagai contoh:
s = '{foo} {bar}'
s.format(foo='FOO') #Problem: raises KeyError 'bar'
python
string-formatting
P3trus
sumber
sumber
{bar:1.2f}
__missing__()
, kembalikan instance kelas kustom yang menimpa__format__()
dengan cara mengembalikan placeholder asli termasuk spesifikasi format. Bukti konsep: ideone.com/xykV7RJika Anda tahu dalam urutan apa Anda memformat sesuatu:
Gunakan seperti ini:
Anda tidak dapat menentukan
foo
danbar
pada saat yang sama - Anda harus melakukannya secara berurutan.sumber
s.format(foo='FOO',bar='BAR')
maka saya masih dapat'FOO {bar}'
, apa pun yang terjadi. Bisakah Anda menjelaskannya?Anda bisa menggunakan
partial
fungsifunctools
yang pendek, paling mudah dibaca dan juga menjelaskan maksud pembuat kode:sumber
python from functool import partial s = "{foo} {bar}".format s_foo = partial(s, foo="FOO") print(s_foo(bar="BAR")) # FOO BAR print(s(foo="FOO", bar="BAR")) # FOO BAR
partial()
tidak akan membantu saya jika saya perlu melakukan beberapa pemrosesan dengan string yang sebagian diformat (yaitu"FOO {bar}"
)."{foo} {{bar}}".format(foo="{bar}").format(bar="123")
dari contoh lainnya. Saya harapkan"{bar} 123"
tetapi mereka output"123 123"
.Keterbatasan ini
.format()
- ketidakmampuan untuk melakukan pergantian sebagian - telah mengganggu saya.Setelah mengevaluasi penulisan
Formatter
kelas khusus seperti yang dijelaskan dalam banyak jawaban di sini dan bahkan mempertimbangkan menggunakan paket pihak ketiga seperti lazy_format , saya menemukan solusi inbuilt yang lebih sederhana: Templat stringIni memberikan fungsionalitas yang serupa tetapi juga menyediakan
safe_substitute()
metode menyeluruh penggantian sebagian . String template harus memiliki$
awalan (yang terasa agak aneh - tetapi solusi keseluruhan saya pikir lebih baik).Membentuk pembungkus yang nyaman berdasarkan ini:
Demikian pula pembungkus berdasarkan jawaban Sven yang menggunakan pemformatan string default:
sumber
Tidak yakin apakah ini ok sebagai solusi cepat, tapi bagaimana
? :)
sumber
Jika Anda menentukan sendiri
Formatter
yang menimpaget_value
metode, Anda bisa menggunakannya untuk memetakan nama bidang yang tidak ditentukan untuk apa pun yang Anda inginkan:http://docs.python.org/library/string.html#string.Formatter.get_value
Misalnya, Anda bisa memetakan
bar
untuk"{bar}"
jikabar
tidak di kwargs.Namun, itu mengharuskan menggunakan
format()
metode objek Formatter Anda, bukan metode stringformat()
.sumber
Coba ini.
sumber
{{
dan}}
merupakan cara keluar dari tanda pemformatan, sehinggaformat()
tidak melakukan subtitusi dan menggantikan{{
dan}}
dengan{
dan}
, masing-masing.{{ }}
hanya berfungsi untuk satu format, jika Anda perlu menerapkan lebih banyak, Anda perlu menambahkan lebih banyak{}
. ex.'fd:{uid}:{{topic_id}}'.format(uid=123).format(a=1)
akan mengembalikan kesalahan karena format kedua tidak memberikantopic_id
nilai.Berkat komentar Amber , saya datang dengan ini:
sumber
{field!s: >4}
menjadi{field}
Bagi saya ini cukup baik:
sumber
Semua solusi yang saya temukan tampaknya memiliki masalah dengan spesifikasi atau opsi konversi yang lebih canggih. @ SvenMarnach's FormatPlaceholder sangat pintar tetapi tidak bekerja dengan baik dengan paksaan (misalnya
{a!s:>2s}
) karena ia memanggil__str__
metode (dalam contoh ini) alih-alih__format__
dan Anda kehilangan pemformatan tambahan.Inilah yang akhirnya saya dapatkan dan beberapa fitur utamanya:
str.format
(bukan hanya pemetaan){k!s}
{!r}
{k:>{size}}
{k.foo}
{k[0]}
{k!s:>{size}}
Saya menemukan masalah dengan berbagai implementasi setelah menulis beberapa tes tentang bagaimana saya ingin metode ini berperilaku. Mereka ada di bawah jika ada yang menganggapnya berwawasan luas.
sumber
Saran saya adalah sebagai berikut (diuji dengan Python3.6):
Pembaruan: Cara yang lebih elegan (subclassing
dict
dan overloading__missing__(self, key)
) ditunjukkan di sini: https://stackoverflow.com/a/17215533/333403sumber
Dengan asumsi Anda tidak akan menggunakan string sampai benar-benar diisi, Anda bisa melakukan sesuatu seperti kelas ini:
Contoh:
sumber
Ada satu cara lagi untuk mencapai ini yaitu dengan menggunakan
format
dan%
mengganti variabel. Sebagai contoh:sumber
Solusi yang sangat jelek tapi paling sederhana bagi saya adalah dengan hanya melakukan:
Dengan cara ini Anda masih dapat menggunakan
tmpl
sebagai templat biasa dan melakukan pemformatan parsial hanya jika diperlukan. Saya menemukan masalah ini terlalu sepele untuk menggunakan solusi berlebihan seperti Mohan Raj.sumber
Setelah menguji solusi yang paling menjanjikan dari sana - sini , saya menyadari bahwa tidak ada satupun yang benar-benar memenuhi persyaratan berikut:
str.format_map()
untuk templat;Jadi, saya menulis solusi sendiri, yang memenuhi persyaratan di atas. ( EDIT : sekarang versi oleh @SvenMarnach - seperti yang dilaporkan dalam jawaban ini - tampaknya menangani kasus sudut yang saya butuhkan).
Pada dasarnya, saya akhirnya mem-parsing string templat, menemukan
{.*?}
grup bersarang yang cocok (menggunakanfind_all()
fungsi helper) dan membangun string yang diformat secara progresif dan langsung digunakanstr.format_map()
sambil menangkap potensi apa punKeyError
.(Kode ini juga tersedia di FlyingCircus - DISCLAIMER: Saya adalah penulis utamanya.)
Penggunaan kode ini adalah:
Mari kita bandingkan ini dengan solusi favorit saya (oleh @SvenMarnach yang dengan ramah membagikan kodenya di sana - sini ):
Berikut ini beberapa tes:
dan kode untuk membuatnya berjalan:
yang menghasilkan:
seperti yang Anda lihat, versi yang diperbarui sekarang tampaknya menangani dengan baik kasus sudut di mana versi sebelumnya gagal.
Saat itu, mereka berada dalam sekitar. 50% satu sama lain, tergantung pada
text
format aktual (dan kemungkinan aktualsource
), tetapisafe_format_map()
tampaknya memiliki keunggulan dalam sebagian besar tes yang saya lakukan (apa pun artinya, tentu saja):sumber
{d[x]}
bukan string format yang valid sejauh yang saya ketahui.field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
dan keduanyastr.format()
sertastr.format_map()
memahaminya. Saya akan mengatakan ada cukup bukti untuk ini menjadi format string yang valid.str.format()
dengan indeks non-integer dalam tanda kurung? Saya hanya bisa membuat indeks integer berfungsi.a = dict(b='YAY!'); '{a[b]}'.format_map(dict(a=a))
membuat Anda `'YAY!'a[b]
dalam kode Python, tetapi sebenarnyaa["b"]
Terima kasih!Jika Anda ingin membongkar kamus untuk meneruskan argumen
format
, seperti dalam pertanyaan terkait ini , Anda dapat menggunakan metode berikut.Pertama-tama anggap string
s
itu sama seperti dalam pertanyaan ini:dan nilai-nilai diberikan oleh kamus berikut:
Jelas ini tidak akan berhasil:
Namun, pertama-tama Anda bisa mendapatkan
set
dari semua argumen bernamas
dan membuat kamus yang memetakan argumen itu sendiri dibungkus dengan kurung kurawal:Sekarang gunakan
args
kamus untuk mengisi kunci yang hilangreplacements
. Untuk python 3.5+, Anda bisa melakukan ini dalam satu ekspresi :Untuk versi python yang lebih lama, Anda bisa menelepon
update
:sumber
Saya suka jawaban @ sven-marnach. Jawaban saya hanyalah versi panjangnya. Ini memungkinkan pemformatan non-kata kunci dan mengabaikan kunci tambahan. Berikut adalah contoh penggunaan (nama fungsi adalah referensi ke format python 3.6 f-string):
Dan ini kode saya:
sumber
Jika Anda melakukan banyak template dan menemukan fungsionalitas templating string bawaan Python tidak mencukupi atau kikuk, lihat Jinja2 .
Dari dokumen:
sumber
Membaca komentar @Sam Bourne, saya memodifikasi kode @ SvenMarnach agar berfungsi dengan baik dengan paksaan (seperti
{a!s:>2s}
) tanpa menulis parser khusus. Ide dasarnya bukan untuk mengkonversi ke string tetapi menggabungkan kunci yang hilang dengan tag paksaan.Gunakan (misalnya) seperti ini
Tes berikut (terinspirasi oleh tes dari @ norok2) memeriksa output untuk tradisional
format_map
dansafe_format_map
berdasarkan pada kelas di atas dalam dua kasus: memberikan kata kunci yang benar atau tanpa mereka.Output yang mana
sumber
Anda bisa membungkusnya dalam fungsi yang mengambil argumen default:
sumber