it-swarm-id.com

Pemformatan string Python:% vs. .format

Python 2.6 memperkenalkan metode str.format() dengan sintaks yang sedikit berbeda dari operator % yang ada. Mana yang lebih baik dan untuk situasi apa?

  1. 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!"
    
  2. Selanjutnya kapan pemformatan string terjadi dalam Python? Misalnya, jika level logging saya diatur ke TINGGI apakah saya akan tetap dapat melakukan operasi % berikut? Dan jika demikian, adakah cara untuk menghindari ini?

    log.debug("some debug info: %s" % some_info)
    
1237
NorthIsUp

Untuk menjawab pertanyaan pertama Anda ... .format sepertinya lebih canggih dalam banyak hal. Suatu hal yang menjengkelkan tentang % juga bagaimana ia dapat mengambil variabel atau Tuple. Anda akan berpikir hal berikut akan selalu berhasil:

"hi there %s" % name

namun, jika name kebetulan (1, 2, 3), itu akan melempar TypeError. Untuk menjamin bahwa selalu dicetak, Anda harus melakukannya

"hi there %s" % (name,)   # supply the single argument as a single-item Tuple

yang hanya jelek. .format tidak memiliki masalah tersebut. Juga dalam contoh kedua yang Anda berikan, contoh .format terlihat jauh lebih bersih.

Mengapa Anda tidak menggunakannya? 

  • tidak mengetahuinya (saya sebelum membaca ini)
  • harus kompatibel dengan Python 2.5

Untuk menjawab pertanyaan kedua Anda, pemformatan string terjadi pada saat yang sama dengan operasi lainnya - ketika ekspresi pemformatan string dievaluasi. Dan Python, bukan bahasa yang malas, mengevaluasi ekspresi sebelum memanggil fungsi, jadi dalam contoh log.debug Anda, ekspresi "some debug info: %s"%some_info pertama-tama akan dievaluasi, misalnya "some debug info: roflcopters are active", maka string itu akan diteruskan ke log.debug()

888
Claudiu

Sesuatu yang tidak bisa dilakukan oleh operator modulo (%), afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

hasil

12 22222 45 22222 103 22222 6 22222

Sangat berguna.

Poin lain: format(), sebagai fungsi, dapat digunakan sebagai argumen dalam fungsi lain: 

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

Hasil dalam:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00
289
eyquem

Dengan asumsi Anda menggunakan modul logging Python, Anda bisa meneruskan argumen pemformatan string sebagai argumen ke metode .debug() daripada melakukan pemformatan sendiri:

log.debug("some debug info: %s", some_info)

yang menghindari melakukan format kecuali logger benar-benar mencatat sesuatu.

132
Wooble

Pada Python 3.6 (2016) Anda dapat menggunakan f-string untuk mengganti variabel:

>>> Origin = "London"
>>> destination = "Paris"
>>> f"from {Origin} to {destination}"
'from London to Paris'

Perhatikan awalan f". Jika Anda mencoba ini dengan Python 3.5 atau lebih awal, Anda akan mendapatkan SyntaxError.

Lihat https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

98
Colonel Panic

PEP 3101 mengusulkan penggantian operator % dengan pemformatan string canggih baru dalam Python 3, di mana ia akan menjadi default.

55
BrainStorm

Tetapi harap berhati-hati, baru saja saya menemukan satu masalah ketika mencoba mengganti semua % dengan .format dalam kode yang ada:'{}'.format(unicode_string) akan mencoba menyandikan unicode_string dan mungkin akan gagal.

Lihat log sesi interaktif Python ini:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s hanyalah sebuah string (disebut 'byte array' dalam Python3) dan u adalah string Unicode (disebut 'string' dalam Python3):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

Ketika Anda memberikan objek Unicode sebagai parameter ke operator %, ia akan menghasilkan string Unicode bahkan jika string asli bukan Unicode:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

tetapi fungsi .format akan memunculkan "UnicodeEncodeError":

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

dan itu akan bekerja dengan argumen Unicode baik-baik saja jika string asli adalah Unicode.

; '{}'.format(u'i')
'i'

atau jika string argumen dapat dikonversi ke string (disebut 'byte array')

51
rslnx

Namun keuntungan lain dari .format (yang tidak saya lihat dalam jawaban): dapat mengambil properti objek.

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

Atau, sebagai argumen kata kunci:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

Ini tidak mungkin dengan % sejauh yang saya tahu.

33
matiasg

Seperti yang saya temukan hari ini, cara lama memformat string melalui % tidak mendukung Decimal, modul Python untuk titik tetap desimal dan aritmatika titik mengambang, di luar kotak.

Contoh (menggunakan Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

Keluaran:

0,00000000000000000000000312375239000000009907464850 0,0000000000000000000000031237523900000000000000000000

Tentunya mungkin ada penyelesaian tetapi Anda masih dapat mempertimbangkan untuk menggunakan metode format() segera. 

28
balu

% memberikan kinerja yang lebih baik daripada format dari pengujian saya.

Kode uji:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

Hasil:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

Hasil

> format: 0.5864730989560485
> %: 0.013593495357781649

Itu terlihat di Python2, perbedaannya kecil sedangkan di Python3, % jauh lebih cepat daripada format.

Terima kasih @Chris Cogdon untuk kode sampel.

25
lcltj

Sebagai catatan tambahan, Anda tidak harus melakukan hit kinerja untuk menggunakan pemformatan gaya baru dengan pencatatan. Anda dapat mengirimkan objek apa pun ke logging.debug, logging.info, dll. Yang mengimplementasikan metode ajaib __str__. Ketika modul logging telah memutuskan bahwa ia harus memancarkan objek pesan Anda (apa pun itu), ia memanggil str(message_object) sebelum melakukannya. Jadi Anda bisa melakukan sesuatu seperti ini:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

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 dari fakta bahwa itu adalah format-agnostik, adalah memungkinkan untuk nilai-nilai malas, mis. fungsi expensive_func di 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 .

14
David Sanders

Satu situasi di mana % dapat membantu adalah ketika Anda memformat ekspresi regex. Sebagai contoh, 

'{type_names} [a-z]{2}'.format(type_names='triangle|square')

memunculkan IndexError. Dalam situasi ini, Anda dapat menggunakan:

'%(type_names)s [a-z]{2}' % {'type_names': 'triangle|square'}

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.

8
Jorge Leitão

Jika python Anda> = 3.6, literal berformat F-string adalah teman baru Anda.

Ini lebih sederhana, bersih, dan kinerjanya lebih baik.

In [1]: params=['Hello', 'adam', 42]

In [2]: %timeit "%s %s, the answer to everything is %d."%(params[0],params[1],params[2])
448 ns ± 1.48 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit "{} {}, the answer to everything is {}.".format(*params)
449 ns ± 1.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [4]: %timeit f"{params[0]} {params[1]}, the answer to everything is {params[2]}."
12.7 ns ± 0.0129 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)
6
zhengcao

Saya akan menambahkan bahwa sejak versi 3.6, kita dapat menggunakan fstrings seperti berikut

foo = "john"
bar = "smith"
print(f"My name is {foo} {bar}")

Yang memberi

Nama saya john smith

Semuanya dikonversi menjadi string

mylist = ["foo", "bar"]
print(f"mylist = {mylist}")

Hasil:

mylist = ['foo', 'bar']

Anda dapat melewati fungsi, seperti dalam metode format lain

print(f'Hello, here is the date : {time.strftime("%d/%m/%Y")}')

Memberi misalnya

Halo, ini tanggalnya: 16/04/2018

4
Sylvan LE DEUNFF

Untuk versi python> = 3.6 (lihat PEP 498 )

s1='albha'
s2='beta'

f'{s1}{s2:>10}'

#output
'albha      beta'
2
Roushan

Python 3.6.7 komparatif:

#!/usr/bin/env python
import timeit

def time_it(fn):
    """
    Measure time of execution of a function
    """
    def wrapper(*args, **kwargs):
        t0 = timeit.default_timer()
        fn(*args, **kwargs)
        t1 = timeit.default_timer()
        print("{0:.10f} seconds".format(t1 - t0))
    return wrapper


@time_it
def new_new_format(s):
    print("new_new_format:", f"{s[0]} {s[1]} {s[2]} {s[3]} {s[4]}")


@time_it
def new_format(s):
    print("new_format:", "{0} {1} {2} {3} {4}".format(*s))


@time_it
def old_format(s):
    print("old_format:", "%s %s %s %s %s" % s)


def main():
    samples = (("uno", "dos", "tres", "cuatro", "cinco"), (1,2,3,4,5), (1.1, 2.1, 3.1, 4.1, 5.1), ("uno", 2, 3.14, "cuatro", 5.5),) 
    for s in samples:
        new_new_format(s)
        new_format(s)
        old_format(s)
        print("-----")


if __== '__main__':
    main()

Keluaran:

new_new_format: uno dos tres cuatro cinco
0.0000170280 seconds
new_format: uno dos tres cuatro cinco
0.0000046750 seconds
old_format: uno dos tres cuatro cinco
0.0000034820 seconds
-----
new_new_format: 1 2 3 4 5
0.0000043980 seconds
new_format: 1 2 3 4 5
0.0000062590 seconds
old_format: 1 2 3 4 5
0.0000041730 seconds
-----
new_new_format: 1.1 2.1 3.1 4.1 5.1
0.0000092650 seconds
new_format: 1.1 2.1 3.1 4.1 5.1
0.0000055340 seconds
old_format: 1.1 2.1 3.1 4.1 5.1
0.0000052130 seconds
-----
new_new_format: uno 2 3.14 cuatro 5.5
0.0000053380 seconds
new_format: uno 2 3.14 cuatro 5.5
0.0000047570 seconds
old_format: uno 2 3.14 cuatro 5.5
0.0000045320 seconds
-----
1
Felix Martinez

Tetapi satu hal adalah itu juga jika Anda memiliki kurung kurawal bersarang, tidak akan berfungsi untuk format tetapi % akan berfungsi.

Contoh:

>>> '{{0}, {1}}'.format(1,2)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    '{{0}, {1}}'.format(1,2)
ValueError: Single '}' encountered in format string
>>> '{%s, %s}'%(1,2)
'{1, 2}'
>>> 
1
U9-Forward

Benar-benar terlihat kita benar-benar jauh dari topik aslinya, tetapi mengapa tidak: 

Saat menggunakan modul gettext untuk memberikan mis. GUI lokal, string gaya lama dan baru adalah satu-satunya cara; f-string tidak bisa digunakan di sana. IMHO gaya baru adalah pilihan terbaik untuk kasus ini. Ada SO pertanyaan tentang ini di sini .

0
jake77