Saya menggunakan string template untuk menghasilkan beberapa file dan saya menyukai keringkasan f-string baru untuk tujuan ini, untuk mengurangi kode template saya sebelumnya dari sesuatu seperti ini:
template_a = "The current name is {name}"
names = ["foo", "bar"]
for name in names:
print (template_a.format(**locals()))
Sekarang saya bisa melakukan ini, langsung mengganti variabel:
names = ["foo", "bar"]
for name in names:
print (f"The current name is {name}")
Namun, terkadang masuk akal untuk menetapkan template di tempat lain - lebih tinggi dalam kode, atau diimpor dari file atau sesuatu. Artinya, template adalah string statis dengan tag pemformatan di dalamnya. Sesuatu harus terjadi pada string untuk memberi tahu penerjemah untuk menafsirkan string sebagai f-string baru, tetapi saya tidak tahu apakah ada hal seperti itu.
Apakah ada cara untuk memasukkan string dan menafsirkannya sebagai f-string untuk menghindari penggunaan .format(**locals())
panggilan?
Idealnya saya ingin dapat membuat kode seperti ini ... (di magic_fstring_function
mana bagian yang tidak saya mengerti masuk):
template_a = f"The current name is {name}"
# OR [Ideal2] template_a = magic_fstring_function(open('template.txt').read())
names = ["foo", "bar"]
for name in names:
print (template_a)
... dengan keluaran yang diinginkan ini (tanpa membaca file dua kali):
The current name is foo
The current name is bar
... tapi keluaran sebenarnya yang saya dapatkan adalah:
The current name is {name}
The current name is {name}
f
tali. Sebuahf
string tidak data, dan itu tentu tidak string; itu kode. (Periksa dengandis
modul.) Jika Anda ingin kode dievaluasi di lain waktu, Anda menggunakan fungsi..format(**locals())
, meskipun secara kosmetik lebih bagus. Sampai PEP-501 diimplementasikan.str.format()
yang mendukung evaluasi yang ditangguhkan di satu sisi dan sintaks f-string yang lebih lengkap dan sangat cepat yang tidak mendukung evaluasi yang ditangguhkan di sisi lain. Jadi kita masih membutuhkan keduanya dan Python masih belum memiliki pemformat string standar. Masukkan meme standar xkcd.Jawaban:
Berikut adalah "Ideal 2" yang lengkap.
Ini bukan f-string — bahkan tidak menggunakan f-string — tapi seperti yang diminta. Sintaks persis seperti yang ditentukan. Tidak ada sakit kepala keamanan karena kami tidak menggunakan
eval()
.Ini menggunakan kelas kecil dan implementasi
__str__
yang secara otomatis dipanggil dengan print. Untuk keluar dari ruang lingkup kelas yang terbatas, kita menggunakaninspect
modul untuk melompat satu frame ke atas dan melihat variabel yang dapat diakses pemanggil.import inspect class magic_fstring_function: def __init__(self, payload): self.payload = payload def __str__(self): vars = inspect.currentframe().f_back.f_globals.copy() vars.update(inspect.currentframe().f_back.f_locals) return self.payload.format(**vars) template = "The current name is {name}" template_a = magic_fstring_function(template) # use it inside a function to demonstrate it gets the scoping right def new_scope(): names = ["foo", "bar"] for name in names: print(template_a) new_scope() # The current name is foo # The current name is bar
sumber
template = "The beginning of the name is {name[:4]}"
(->TypeError: string indices must be integers
)str.format
. Dulu saya berpikir f-string hanyalah gula sintaksis untuk sesuatu sepertistr.format(**locals(), **globals())
tapi jelas saya salah.inspect
adalah bendera merah.__slots__
sini untuk penggunaan memori yang berkurang?Ya, itulah mengapa kami memiliki literal dengan bidang pengganti dan
.format
, sehingga kami dapat mengganti bidang kapan pun kami suka dengan memanggilnyaformat
.Itu awalannya
f/F
. Anda dapat menggabungkannya dalam sebuah fungsi dan menunda evaluasi selama waktu panggilan, tetapi tentu saja itu menimbulkan overhead tambahan:template_a = lambda: f"The current name is {name}" names = ["foo", "bar"] for name in names: print (template_a())
Yang mencetak:
The current name is foo The current name is bar
tetapi terasa salah dan dibatasi oleh fakta bahwa Anda hanya dapat mengintip namespace global di pengganti Anda. Mencoba menggunakannya dalam situasi yang membutuhkan nama lokal akan gagal total kecuali diteruskan ke string sebagai argumen (yang benar-benar mengalahkan intinya).
Selain fungsi (termasuk batasan), tidak, jadi sebaiknya tetap gunakan
.format
.sumber
Cara ringkas agar string dievaluasi sebagai f-string (dengan kemampuan penuhnya) adalah menggunakan fungsi berikut:
def fstr(template): return eval(f"f'{template}'")
Kemudian Anda dapat melakukan:
template_a = "The current name is {name}" names = ["foo", "bar"] for name in names: print(fstr(template_a)) # The current name is foo # The current name is bar
Dan, berbeda dengan banyak solusi lain yang diusulkan, Anda juga dapat melakukan:
template_b = "The current name is {name.upper() * 2}" for name in names: print(fstr(template_b)) # The current name is FOOFOO # The current name is BARBAR
sumber
name
bersifat global. f-string harus ditangguhkan dalam evaluasi, tetapi kelas FString perlu membuat daftar referensi ke argumen terbatas dengan melihat pemanggil lokal dan global ... dan kemudian mengevaluasi string tersebut saat digunakan.eval()
umumnya tidak disarankan.F-string hanyalah cara yang lebih ringkas untuk membuat string berformat, menggantikan
.format(**names)
denganf
. Jika Anda tidak ingin string segera dievaluasi dengan cara seperti itu, jangan menjadikannya f-string. Simpan sebagai string literal biasa, dan kemudian panggilformat
nanti ketika Anda ingin melakukan interpolasi, seperti yang telah Anda lakukan.Tentu saja, ada alternatif lain dengan
eval
.template.txt
:Kode:
>>> template_a = open('template.txt').read() >>> names = 'foo', 'bar' >>> for name in names: ... print(eval(template_a)) ... The current name is foo The current name is bar
Tetapi yang berhasil Anda lakukan hanyalah mengganti
str.format
denganeval
, yang tentunya tidak sepadan. Tetap gunakan string biasa denganformat
panggilan.sumber
The current name is {name}
di dalamtemplate.txt
file dan kemudian menggunakanprint(template_a.format(name=name))
(atau.format(**locals())
). Kode ini lebih panjang sekitar 10 karakter, tetapi tidak menimbulkan kemungkinan masalah keamanan karenaeval
.eval
memungkinkan kita untuk menulisf'{name}'
dan menunda evaluasiname
sampai diinginkan, itu inferior untuk hanya membuat string template biasa dan kemudian memanggilnyaformat
, seperti yang sudah dilakukan OP..format
tidak setara dengan f-string, yang dapat mendukung Anda komentar:DNA = "TATTCGCGGAAAATATTTTGA"; fragment = f"{DNA[2:8]}"; failed_fragment = "{DNA[2:8]}".format(**locals())
. Upaya untuk menciptakanfailed_fragment
hasil dalamTypeError: string indices must be integers
.Menggunakan .format bukanlah jawaban yang benar untuk pertanyaan ini. F-string Python sangat berbeda dari template str.format () ... mereka dapat berisi kode atau operasi mahal lainnya - oleh karena itu perlu penangguhan.
Berikut ini contoh logger yang ditangguhkan. Ini menggunakan pembukaan normal logging.getLogger, tetapi kemudian menambahkan fungsi baru yang menafsirkan f-string hanya jika level log sudah benar.
log = logging.getLogger(__name__) def __deferred_flog(log, fstr, level, *args): if log.isEnabledFor(level): import inspect frame = inspect.currentframe().f_back.f_back try: fstr = 'f"' + fstr + '"' log.log(level, eval(fstr, frame.f_globals, frame.f_locals)) finally: del frame log.fdebug = lambda fstr, *args: __deferred_flog(log, fstr, logging.DEBUG, *args) log.finfo = lambda fstr, *args: __deferred_flog(log, fstr, logging.INFO, *args)
Ini memiliki keuntungan karena dapat melakukan hal-hal seperti:
log.fdebug("{obj.dump()}")
.... tanpa membuang objek kecuali proses debug diaktifkan.IMHO: Ini seharusnya menjadi default operasi f-string, namun sekarang sudah terlambat . Evaluasi F-string dapat memiliki efek samping yang sangat besar dan tidak diinginkan, dan jika hal itu terjadi secara tertunda akan mengubah eksekusi program.
Untuk membuat f-string ditangguhkan dengan benar, python memerlukan beberapa cara untuk mengubah perilaku secara eksplisit. Mungkin menggunakan huruf 'g'? ;)
Telah ditunjukkan bahwa logging yang ditangguhkan seharusnya tidak macet jika ada bug di konverter string. Solusi di atas dapat melakukan ini juga, mengubah
finally:
menjadiexcept:
, dan menempelkan alog.exception
di sana.sumber
%timeit log.finfo(f"{bar=}") 91.9 µs ± 7.45 µs per loop %timeit log.info(f"{bar=}") 56.2 µs ± 630 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) log.setLevel(logging.CRITICAL) %timeit log.finfo("{bar=}") 575 ns ± 2.9 ns per loop %timeit log.info(f"{bar=}") 480 ns ± 9.37 ns per loop %timeit log.finfo("") 571 ns ± 2.66 ns per loop %timeit log.info(f"") 380 ns ± 0.92 ns per loop %timeit log.info("") 367 ns ± 1.65 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Apa yang Anda inginkan tampaknya dianggap sebagai peningkatan Python .
Sementara itu - dari diskusi yang ditautkan - berikut ini sepertinya merupakan solusi yang masuk akal yang tidak memerlukan penggunaan
eval()
:class FL: def __init__(self, func): self.func = func def __str__(self): return self.func() template_a = FL(lambda: f"The current name, number is {name!r}, {number+1}") names = "foo", "bar" numbers = 40, 41 for name, number in zip(names, numbers): print(template_a)
Keluaran:
sumber
terinspirasi oleh jawaban oleh kadee , berikut ini dapat digunakan untuk mendefinisikan kelas f-string yang ditangguhkan.
class FStr: def __init__(self, s): self._s = s def __repr__(self): return eval(f"f'{self._s}'") ... template_a = FStr('The current name is {name}') names = ["foo", "bar"] for name in names: print (template_a)
itulah pertanyaan yang ditanyakan
sumber
Atau mungkin tidak menggunakan f-string, cukup format:
fun = "The curent name is {name}".format names = ["foo", "bar"] for name in names: print(fun(name=name))
Dalam versi tanpa nama:
fun = "The curent name is {}".format names = ["foo", "bar"] for name in names: print(fun(name))
sumber
fun = "{DNA[2:8]}".format; DNA = "TATTCGCGGAAAATATTTTGA"; fun(DNA=DNA)
. ->TypeError: string indices must be integers
Bagaimana tentang:
s = 'Hi, {foo}!' s > 'Hi, {foo}!' s.format(foo='Bar') > 'Hi, Bar!'
sumber
Saran yang menggunakan f-string. Lakukan evaluasi Anda pada tingkat logis di mana template terjadi dan berikan sebagai generator. Anda dapat melepasnya kapan saja, menggunakan f-string
In [46]: names = (i for i in ('The CIO, Reed', 'The homeless guy, Arnot', 'The security guard Spencer')) In [47]: po = (f'Strangely, {next(names)} has a nice {i}' for i in (" nice house", " fast car", " big boat")) In [48]: while True: ...: try: ...: print(next(po)) ...: except StopIteration: ...: break ...: Strangely, The CIO, Reed has a nice nice house Strangely, The homeless guy, Arnot has a nice fast car Strangely, The security guard Spencer has a nice big boat
sumber