Python 2.6 memperkenalkan str.format()
metode ini dengan sintaks yang sedikit berbeda dari %
operator yang ada . Mana yang lebih baik dan untuk situasi apa?
Berikut ini menggunakan setiap metode dan memiliki hasil yang sama, jadi apa bedanya?
#!/usr/bin/python sub1 = "python string!" sub2 = "an arg" a = "i am a %s" % sub1 b = "i am a {0}".format(sub1) c = "with %(kwarg)s!" % {'kwarg':sub2} d = "with {kwarg}!".format(kwarg=sub2) print a # "i am a python string!" print b # "i am a python string!" print c # "with an arg!" print d # "with an arg!"
Selanjutnya kapan pemformatan string terjadi dalam Python? Misalnya, jika tingkat logging saya diatur ke TINGGI akankah saya masih terpukul untuk melakukan
%
operasi berikut ? Dan jika demikian, adakah cara untuk menghindari ini?log.debug("some debug info: %s" % some_info)
python
performance
logging
string-formatting
f-string
NorthIsUp
sumber
sumber
%
gaya lama lebih sering, karena jika Anda tidak memerlukan peningkatan kemampuanformat()
gaya,%
gaya sering jauh lebih nyaman.format()
gaya format dan lebih tua%
gaya format berbasis .Jawaban:
Untuk menjawab pertanyaan pertama Anda ...
.format
sepertinya lebih canggih dalam banyak hal. Suatu hal yang menjengkelkan tentang%
juga bagaimana bisa mengambil variabel atau tuple. Anda akan berpikir hal berikut akan selalu berhasil:namun, jika
name
kebetulan(1, 2, 3)
, itu akan melemparTypeError
. Untuk menjamin bahwa selalu dicetak, Anda harus melakukannyayang hanya jelek.
.format
tidak memiliki masalah itu. Juga dalam contoh kedua yang Anda berikan,.format
contohnya tampak jauh lebih bersih.Mengapa Anda tidak menggunakannya?
Untuk menjawab pertanyaan kedua Anda, pemformatan string terjadi pada saat yang sama dengan operasi lainnya - ketika ekspresi pemformatan string dievaluasi. Dan Python, bukan menjadi bahasa yang malas, mengevaluasi ekspresi sebelum memanggil fungsi, jadi dalam
log.debug
contoh Anda , ekspresi"some debug info: %s"%some_info
pertama akan mengevaluasi, misalnya"some debug info: roflcopters are active"
, kemudian string itu akan diteruskan kelog.debug()
.sumber
"%(a)s, %(a)s" % {'a':'test'}
log.debug("something: %s" % x)
tetapi tidak untuklog.debug("something: %s", x)
Pemformatan string akan ditangani dalam metode dan Anda tidak akan mendapatkan kinerja yang baik jika itu tidak akan dicatat. Seperti biasa, Python mengantisipasi kebutuhan Anda =)'{0}, {0}'.format('test')
.man sprintf
dan pelajari tentang$
notasi di dalam%
placeholderprintf("%2$d", 1, 3)
untuk mencetak "3", itu ditentukan dalam POSIX, bukan C99. Halaman manual yang Anda referensikan mencatat, "Standar C99 tidak termasuk gaya menggunakan '$' ...".Sesuatu yang tidak bisa dilakukan operator modulo (%), afaik:
hasil
Sangat berguna.
Poin lain:,
format()
sebagai fungsi, dapat digunakan sebagai argumen dalam fungsi lain:Hasil dalam:
sumber
map
semudah memformat.map('some_format_string_%s'.__mod__, some_iterable)
printf("%2$s %1$s\n", "One", "Two");
dikompilasi dengangcc -std=c99 test.c -o test
, hasilnyaTwo One
. Tapi saya berdiri dikoreksi: Ini sebenarnya adalah ekstensi POSIX dan bukan C. Saya tidak dapat menemukannya lagi dalam standar C / C ++, di mana saya pikir saya telah melihatnya. Kode berfungsi bahkan dengan bendera std 'c90'.sprintf
halaman manual . Ini tidak mencantumkannya, tetapi memungkinkan lib untuk mengimplementasikan superset. Argumen asli saya masih valid, digantiC
denganPosix
%
untuk memesan kembali placeholder. Saya masih ingin tidak menghapus komentar pertama itu demi konsistensi komentar di sini. Saya minta maaf karena telah melampiaskan kemarahan saya di sini. Hal ini diarahkan terhadap pernyataan yang sering dibuat bahwa sintaksis lama tidak akan mengizinkan hal ini. Alih-alih membuat sintaks yang sama sekali baru, kita bisa memperkenalkan ekstensi std Posix. Kita bisa memiliki keduanya.Dengan asumsi Anda menggunakan
logging
modul Python , Anda bisa meneruskan argumen pemformatan string sebagai argumen ke.debug()
metode daripada melakukan pemformatan sendiri:yang menghindari melakukan format kecuali logger benar-benar mencatat sesuatu.
sumber
log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry'))
Namun, Anda tidak dapat menggunakan.format()
sintaks gaya baru di sini, bahkan dalam Python 3.3, yang memalukan.Pada Python 3.6 (2016) Anda bisa menggunakan f-string untuk mengganti variabel:
Perhatikan
f"
awalannya. Jika Anda mencoba ini dengan Python 3.5 atau sebelumnya, Anda akan mendapatkanSyntaxError
.Lihat https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings
sumber
PEP 3101 mengusulkan penggantian
%
operator dengan pemformatan string canggih baru dalam Python 3, di mana ia akan menjadi default.sumber
.format
tidak akan mengganti%
pemformatan string.Tapi harap berhati-hati, baru saja saya menemukan satu masalah ketika mencoba mengganti semua
%
dengan.format
kode yang ada:'{}'.format(unicode_string)
akan mencoba menyandikan unicode_string dan mungkin akan gagal.Lihat log sesi interaktif Python ini:
s
hanya string (disebut 'byte array' di Python3) danu
merupakan string Unicode (disebut 'string' dalam Python3):Ketika Anda memberikan objek Unicode sebagai parameter kepada
%
operator, ia akan menghasilkan string Unicode bahkan jika string asli bukan Unicode:tetapi
.format
fungsinya akan memunculkan "UnicodeEncodeError":dan itu akan bekerja dengan argumen Unicode baik-baik saja jika string asli adalah Unicode.
atau jika string argumen dapat dikonversi ke string (disebut 'byte array')
sumber
format
metode baru benar-benar diperlukan ...%
interpolasi string akan hilang."p1=%s p2=%d" % "abc", 2
atau"p1=%s p2=%s" % (tuple_p1_p2,)
. Anda mungkin berpikir itu adalah kesalahan pembuat kode, tetapi saya pikir itu hanya sintaks yang aneh aneh yang terlihat bagus untuk quicky-scriptie tetapi buruk untuk kode produksi.%s
,%02d
seperti"p1=%s p2=%02d".format("abc", 2)
. Saya menyalahkan mereka yang menemukan dan menyetujui format kurung kurawal yang mengharuskan Anda untuk melarikan diri dari mereka{{}}
dan terlihat imho jelek.Namun keuntungan lain dari
.format
(yang saya tidak melihat dalam jawaban): dapat mengambil properti objek.Atau, sebagai argumen kata kunci:
Ini tidak mungkin dengan
%
sejauh yang saya tahu.sumber
'x is {0}, y is {1}'.format(a.x, a.y)
. Seharusnya hanya digunakan saata.x
operasi sangat mahal.'x is {a.x}, y is {a.y}'.format(a=a)
. Lebih mudah dibaca daripada kedua contoh.'x is {a.x}, y is {a.y}'.format(**vars())
'{foo[bar]}'.format(foo={'bar': 'baz'})
.Your order, number {order[number]} was processed at {now:%Y-%m-%d %H:%M:%S}, will be ready at about {order[eta]:%H:%M:%S}
atau apa pun yang mereka inginkan. Ini jauh lebih bersih daripada mencoba menawarkan fungsionalitas yang sama dengan formatter lama. Itu membuat string format yang disediakan pengguna jauh lebih kuat.%
memberikan kinerja yang lebih baik daripadaformat
dari tes saya.Kode uji:
Python 2.7.2:
Hasil:
Python 3.5.2
Hasil
Itu terlihat di Python2, perbedaannya kecil sedangkan di Python3,
%
jauh lebih cepat daripadaformat
.Terima kasih @Chris Cogdon untuk kode sampel.
Edit 1:
Diuji lagi dengan Python 3.7.2 pada Juli 2019.
Hasil:
Tidak banyak perbedaan. Saya kira Python membaik secara bertahap.
Edit 2:
Setelah seseorang menyebutkan f-string python 3 dalam komentar, saya melakukan tes untuk kode berikut di bawah python 3.7.2:
Hasil:
Tampaknya f-string masih lebih lambat daripada
%
tetapi lebih baik daripadaformat
.sumber
str.format
memberikan lebih banyak fungsionalitas (misalnya pemformatan khusus tipe'{0:%Y-%m-%d}'.format(datetime.datetime.utcnow())
). Kinerja tidak dapat menjadi persyaratan mutlak dari semua pekerjaan. Gunakan alat yang tepat untuk pekerjaan itu.%
operator memungkinkan untuk menggunakan kembaliprintf
pengetahuan; interpolasi kamus adalah perluasan prinsip yang sangat sederhana.Seperti yang saya temukan hari ini, cara lama memformat string melalui
%
tidak mendukungDecimal
, modul Python untuk titik tetap desimal dan aritmatika titik mengambang, di luar kotak.Contoh (menggunakan Python 3.3.5):
Keluaran:
Tentunya mungkin ada penyelesaian tetapi Anda masih dapat segera mempertimbangkan untuk menggunakan
format()
metode ini.sumber
str(d)
sebelum memperluas parameter, sedangkan pemformatan gaya lama mungkin memanggilfloat(d)
terlebih dahulu.str(d)
kembali"3.12375239e-24"
, tidak"0.00000000000000000000000312375239000000000000000000"
Jika python Anda> = 3.6, literal berformat F-string adalah teman baru Anda.
Ini lebih sederhana, bersih, dan kinerjanya lebih baik.
sumber
Sebagai catatan tambahan, Anda tidak perlu melakukan hit kinerja untuk menggunakan pemformatan gaya baru dengan pencatatan. Anda dapat melewati objek apapun untuk
logging.debug
,logging.info
, dll yang mengimplementasikan dengan__str__
metode magic. Ketika modul logging telah memutuskan bahwa ia harus memancarkan objek pesan Anda (apa pun itu), ia memanggilstr(message_object)
sebelum melakukannya. Jadi Anda bisa melakukan sesuatu seperti ini:Ini semua dijelaskan dalam dokumentasi Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ). Namun, ini juga akan bekerja dengan Python 2.6 ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages ).
Salah satu keuntungan menggunakan teknik ini, selain fakta bahwa itu adalah format-agnostik, adalah memungkinkan untuk nilai-nilai malas misalnya fungsi di
expensive_func
atas. Ini memberikan alternatif yang lebih elegan untuk saran yang diberikan dalam dokumen Python di sini: https://docs.python.org/2.6/library/logging.html#optimization .sumber
format
tanpa kinerja hit - melakukannya dengan menimpa__str__
tepat sepertilogging
yang dirancang untuk - mempersingkat pemanggilan fungsi ke satu huruf (N
) yang terasa sangat mirip dengan beberapa cara standar untuk mendefinisikan string - DAN memungkinkan untuk malas pemanggilan fungsi. Terima kasih! +1logging.Formatter(style='{')
parameter?Satu situasi di mana
%
dapat membantu adalah ketika Anda memformat ekspresi regex. Sebagai contoh,menimbulkan
IndexError
. Dalam situasi ini, Anda dapat menggunakan:Ini menghindari penulisan regex sebagai
'{type_names} [a-z]{{2}}'
. Ini bisa berguna ketika Anda memiliki dua regex, di mana satu digunakan sendiri tanpa format, tetapi gabungan keduanya diformat.sumber
'{type_names} [a-z]{{2}}'.format(type_names='triangle|square')
. Ini seperti mengatakan.format()
dapat membantu ketika menggunakan string yang sudah mengandung karakter persen. Tentu. Anda harus melarikan diri dari mereka."One situation where % may help is when you are formatting regex expressions."
Secara khusus, anggaplaha=r"[a-z]{2}"
potongan regex yang akan Anda gunakan dalam dua ekspresi akhir yang berbeda (misalnyac1 = b + a
danc2 = a
). Asumsikan yangc1
perluformat
diedit (mis.b
Perlu diformat runtime), tetapic2
tidak. Maka Anda perlua=r"[a-z]{2}"
untukc2
dana=r"[a-z]{{2}}"
untukc1.format(...)
.Saya akan menambahkan bahwa sejak versi 3.6, kita dapat menggunakan fstrings seperti berikut
Yang memberi
Semuanya dikonversi menjadi string
Hasil:
Anda dapat melewati fungsi, seperti dalam metode format lain
Memberi misalnya
sumber
Untuk versi python> = 3.6 (lihat PEP 498 )
sumber
Python 3.6.7 komparatif:
Keluaran:
sumber
Tetapi satu hal adalah itu juga jika Anda memiliki kurung kurawal, tidak akan berfungsi untuk format tetapi
%
akan berfungsi.Contoh:
sumber