Câu hỏi Định dạng chuỗi Python:% so với .format


Python 2.6 đã giới thiệu str.format() phương pháp với một cú pháp hơi khác so với hiện tại % nhà điều hành. Đó là tốt hơn và cho những tình huống gì?

  1. Sau đây sử dụng mỗi phương pháp và có cùng một kết quả, vậy sự khác biệt là gì?

    #!/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. Hơn nữa khi nào định dạng chuỗi xảy ra trong Python? Ví dụ: nếu mức ghi nhật ký của tôi được đặt thành CAO thì tôi vẫn nhận được một lần truy cập để thực hiện các thao tác sau % hoạt động? Và nếu vậy, có cách nào để tránh điều này không?

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

1178
2018-02-22 18:46


gốc


tương tự như stackoverflow.com/questions/3691975/… - carl
@ S.Lott, điểm là có là không làm việc không xảy ra, nếu một tuyên bố đăng nhập không in tôi muốn không làm việc cho các định dạng chuỗi. - NorthIsUp
Đối với người mới bắt đầu: Đây là một hướng dẫn rất hay dạy cả hai phong cách. Cá nhân tôi sử dụng cũ hơn % phong cách thường xuyên hơn, bởi vì nếu bạn không cần cải thiện khả năng của format() phong cách, % phong cách thường thuận tiện hơn rất nhiều. - Lutz Prechelt
Để tham khảo: tài liệu Python 3 cho mới hơn format() kiểu định dạng và lớn hơn %kiểu định dạng dựa trên. - Lutz Prechelt
Xem thêm: Pythons nhiều cách định dạng chuỗi - gerrit


Các câu trả lời:


Để trả lời câu hỏi đầu tiên của bạn ... .format có vẻ phức tạp hơn theo nhiều cách. Một điều phiền toái về % cũng là cách nó có thể lấy một biến hoặc một bộ tuple. Bạn nghĩ rằng những điều sau đây sẽ luôn hoạt động:

"hi there %s" % name

chưa, nếu name hoá ra là (1, 2, 3), nó sẽ ném một TypeError. Để đảm bảo rằng nó luôn luôn in, bạn cần phải làm

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

mà chỉ là xấu xí. .format không có những vấn đề đó. Cũng trong ví dụ thứ hai bạn đưa ra, .format ví dụ là tìm kiếm sạch hơn nhiều.

Tại sao bạn không sử dụng nó?

  • không biết về nó (tôi trước khi đọc điều này)
  • phải tương thích với Python 2.5

Để trả lời câu hỏi thứ hai của bạn, định dạng chuỗi xảy ra cùng lúc với bất kỳ thao tác nào khác - khi biểu thức định dạng chuỗi được đánh giá. Và Python, không phải là một ngôn ngữ lười biếng, đánh giá các biểu thức trước khi gọi các hàm, vì vậy trong log.debug ví dụ, biểu thức "some debug info: %s"%some_infotrước tiên sẽ đánh giá, ví dụ: "some debug info: roflcopters are active", sau đó chuỗi đó sẽ được chuyển đến log.debug().


852
2018-02-22 18:49



Thế còn "%(a)s, %(a)s" % {'a':'test'} - ted
Lưu ý rằng bạn sẽ lãng phí thời gian cho log.debug("something: %s" % x) nhưng không phải vì log.debug("something: %s", x)  Định dạng chuỗi sẽ được xử lý trong phương thức và bạn sẽ không nhận được hiệu suất trúng nếu nó không được ghi lại. Như mọi khi, Python dự đoán nhu cầu của bạn =) - darkfeline
ted: đó là một hack tồi tệ hơn để làm tương tự như '{0}, {0}'.format('test'). - flying sheep
Vấn đề là: Một đối số lặp lại rằng cú pháp mới cho phép sắp xếp lại các mục là một điểm tranh luận: Bạn có thể làm tương tự với cú pháp cũ. Hầu hết mọi người không biết rằng điều này thực sự đã được xác định trong Ansi C99 Std! Kiểm tra một bản sao gần đây của man sprintf và tìm hiểu về $ ký hiệu bên trong % trình giữ chỗ - cfi
@cfi: Nếu bạn muốn nói gì đó, printf("%2$d", 1, 3) để in ra "3", được chỉ định trong POSIX chứ không phải C99. Trang rất người đàn ông bạn tham chiếu ghi chú, "Tiêu chuẩn C99 không bao gồm kiểu sử dụng '$' ...". - Thanatos


Một cái gì đó mà toán tử modulo (%) không thể làm, afaik:

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

kết quả

12 22222 45 22222 103 22222 6 22222

Rất hữu dụng.

Điểm khác: format(), là một hàm, có thể được sử dụng như một đối số trong các hàm khác:

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))

Kết quả trong:

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

281
2018-06-13 20:20



Bạn có thể sử dụng định dạng kiểu cũ trong map dễ dàng như định dạng. map('some_format_string_%s'.__mod__, some_iterable) - agf
@cfi: hãy chứng minh bạn đúng bằng cách viết lại ví dụ ở trên trong C99 - MarcH
@Tháng Ba: printf("%2$s %1$s\n", "One", "Two"); biên soạn với gcc -std=c99 test.c -o test, đầu ra là Two One. Nhưng tôi đã sửa chữa: Nó thực sự là một phần mở rộng POSIX và không C. Tôi không thể tìm thấy nó một lần nữa trong tiêu chuẩn C / C ++, nơi tôi nghĩ rằng tôi đã nhìn thấy nó. Mã hoạt động ngay cả với cờ std 'c90'. sprintf trang người đàn ông. Điều này không liệt kê nó, nhưng cho phép libs thực hiện một superset. Đối số ban đầu của tôi vẫn hợp lệ, thay thế C với Posix - cfi
Bình luận đầu tiên của tôi ở đây, không áp dụng cho câu trả lời này. Tôi hối hận về cách nói. Trong Python, chúng ta không thể sử dụng toán tử modulo % để sắp xếp lại phần giữ chỗ. Tôi vẫn không muốn xóa nhận xét đầu tiên đó vì lợi ích của tính nhất quán trong nhận xét ở đây. Tôi xin lỗi vì đã trút cơn giận của tôi ở đây. Nó được đưa ra chống lại tuyên bố thường được thực hiện rằng cú pháp cũ sẽ không cho phép điều này. Thay vì tạo một cú pháp hoàn toàn mới, chúng tôi có thể đã giới thiệu các phần mở rộng Posix std. Chúng ta có thể có cả hai. - cfi
'modulo' dùng để chỉ toán tử đánh giá phần dư sau khi chia. trong trường hợp này ký hiệu phần trăm không phải là toán tử modulo. - Octopus


Giả sử bạn đang sử dụng Python logging , bạn có thể chuyển đối số định dạng chuỗi làm đối số cho .debug() phương pháp thay vì tự định dạng:

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

mà tránh làm các định dạng trừ khi logger thực sự đăng nhập một cái gì đó.


125
2018-02-22 19:21



Đây là một số thông tin hữu ích mà tôi vừa mới học được. Thật đáng tiếc là nó không có câu hỏi riêng vì nó có vẻ khác biệt với câu hỏi chính. Tiếc là OP không chia câu hỏi của mình thành hai câu hỏi riêng biệt. - snth
Bạn có thể sử dụng định dạng dict như sau: log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry'))  Tuy nhiên, bạn không thể sử dụng kiểu mới .format() cú pháp ở đây, ngay cả trong Python 3.3, đó là một sự xấu hổ. - Cito
@Cito: Xem phần này: plumberjack.blogspot.co.uk/2010/10/… - Vinay Sajip
Lợi ích chính của việc này không phải là hiệu năng (làm phép nội suy chuỗi sẽ nhanh chóng so với bất kỳ thứ gì bạn đang thực hiện với đầu ra từ ghi nhật ký, ví dụ như hiển thị trong một thiết bị đầu cuối, lưu vào đĩa) Đó là nếu bạn có một trình tổng hợp ghi nhật ký, nó có thể cho bạn biết "bạn có 12 trường hợp của thông báo lỗi này", ngay cả khi tất cả chúng đều có giá trị 'some_info' khác nhau. Nếu định dạng chuỗi được thực hiện trước khi chuyển chuỗi tới log.debug, thì điều này là không thể. Trình tổng hợp chỉ có thể nói "bạn có 12 thông điệp tường trình khác nhau" - Jonathan Hartley
Nếu bạn lo lắng về hiệu suất, hãy sử dụng cú pháp dict {} theo nghĩa đen thay vì sử dụng dict () class instantiation: doughellmann.com/2012/11/… - trojjer


Kể từ Python 3.6 (2016), bạn có thể sử dụng f-strings để thay thế biến:

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

Lưu ý f" tiếp đầu ngữ. Nếu bạn thử điều này trong Python 3.5 hoặc cũ hơn, bạn sẽ nhận được một SyntaxError.

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


88
2018-04-15 11:12





PEP 3101 đề xuất thay thế % toán tử với định dạng chuỗi mới, nâng cao trong Python 3, nơi nó sẽ là mặc định.


54
2017-08-01 03:01



Không đúng: "Khả năng tương thích ngược có thể được duy trì bằng cách để lại các cơ chế hiện có tại chỗ."; tất nhiên, .format sẽ không thay thế  % định dạng chuỗi. - Tobias
Không, giả định BrainStorms là đúng: "dự định thay thế cho '%' hiện tại. Báo giá Tobias có nghĩa là cả hai hệ thống sẽ cùng tồn tại trong một thời gian. RTFPEP - phobie


Nhưng hãy cẩn thận, ngay bây giờ tôi đã phát hiện ra một vấn đề khi cố gắng thay thế tất cả % với .format trong mã hiện có: '{}'.format(unicode_string) sẽ cố gắng mã hóa unicode_string và có thể sẽ thất bại.

Chỉ cần nhìn vào nhật ký phiên tương tác Python này:

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 chỉ là một chuỗi (được gọi là 'mảng byte' trong Python3) và u là một chuỗi Unicode (được gọi là 'chuỗi' trong Python3):

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

Khi bạn đưa một đối tượng Unicode làm tham số % nhà điều hành nó sẽ tạo ra một chuỗi Unicode ngay cả khi chuỗi gốc không phải là 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)

nhưng .format chức năng sẽ tăng "UnicodeEncodeError":

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

và nó sẽ làm việc với một đối số Unicode chỉ tốt nếu chuỗi ban đầu là Unicode.

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

hoặc nếu chuỗi đối số có thể được chuyển đổi thành một chuỗi (được gọi là 'mảng byte')


51
2017-09-03 18:15



Chỉ đơn giản là không có lý do để thay đổi mã làm việc trừ khi các tính năng bổ sung của format phương pháp thực sự cần thiết ... - Tobias
hoàn toàn đồng ý với bạn, Tobias, nhưng đôi khi nó cần thiết khi nâng cấp lên phiên bản Python mới hơn - rslnx
Ví dụ? AFAIK, nó có không bao giờ là cần thiết; Tôi không nghĩ rằng nó có khả năng là % nội suy chuỗi sẽ biến mất. - Tobias
Tôi xem xét hàm .format () an toàn hơn% cho chuỗi. Thường thì tôi thấy những sai lầm của người mới bắt đầu như thế này "p1=%s p2=%d" % "abc", 2 hoặc là "p1=%s p2=%s" % (tuple_p1_p2,). Bạn có thể nghĩ rằng đó là lỗi của coder nhưng tôi nghĩ rằng nó chỉ là cú pháp lỗi lạ mà có vẻ tốt đẹp cho quicky-scriptie nhưng là xấu cho mã sản xuất. - rslnx
Nhưng tôi không thích cú pháp của .format (), tôi sẽ hạnh phúc hơn với tuổi tốt %s, %02d như "p1=%s p2=%02d".format("abc", 2). Tôi đổ lỗi cho những người đã phát minh ra và chấp thuận định dạng dấu ngoặc nhọn mà cần bạn để thoát khỏi chúng như {{}}và trông imho xấu xí. - rslnx


Tuy nhiên, một ưu điểm khác của .format (mà tôi không thấy trong các câu trả lời): nó có thể có tính chất đối tượng.

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'

Hoặc, như một đối số từ khóa:

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

Điều này là không thể với % theo như tôi có thể nói.


33
2017-12-04 18:33



Điều này có vẻ không đọc được hơn cần thiết so với tương đương 'x is {0}, y is {1}'.format(a.x, a.y). Chỉ nên sử dụng khi a.x hoạt động rất tốn kém. - dtheodor
@dtheodor Với tinh chỉnh để sử dụng đối số từ khóa thay vì đối số vị trí ... 'x is {a.x}, y is {a.y}'.format(a=a). Dễ đọc hơn cả hai ví dụ. - CivFan
@CivFan Hoặc, nếu bạn có nhiều hơn một đối tượng, 'x is {a.x}, y is {a.y}'.format(**vars()) - Jack
Cũng lưu ý điều này trong cùng một thời trang: '{foo[bar]}'.format(foo={'bar': 'baz'}). - Antoine Pinsard
Điều này cực kỳ hữu ích cho các ứng dụng hướng về phía khách hàng, nơi ứng dụng của bạn cung cấp một tập hợp các tùy chọn định dạng chuẩn với chuỗi định dạng do người dùng cung cấp. Tôi sử dụng tất cả các thời gian. Ví dụ, tệp cấu hình sẽ có một số thuộc tính "messagestring" mà người dùng có thể cung cấp 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} hoặc bất cứ điều gì họ muốn. Điều này là sạch hơn nhiều so với cố gắng cung cấp các chức năng tương tự với trình định dạng cũ. Nó làm cho chuỗi định dạng do người dùng cung cấp mạnh mẽ hơn. - Taywee


Như tôi đã khám phá hôm nay, cách định dạng chuỗi cũ thông qua % không hỗ trợ Decimal, Mô-đun của Python cho điểm cố định thập phân và số học dấu chấm động, ra khỏi hộp.

Ví dụ (sử dụng 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))

Đầu ra:

0,00000000000000000000000312375239000000009907464850   0,000000000000000000000312375239000000000000000000

Chắc chắn có thể có xung quanh nhưng bạn vẫn có thể cân nhắc việc sử dụng format() phương pháp ngay lập tức.


27
2018-05-13 17:10



Đó có thể là do cuộc gọi định dạng kiểu mới str(d) trước khi mở rộng tham số, trong khi định dạng kiểu cũ có thể là cuộc gọi float(d) Đầu tiên. - David Sanders
Bạn sẽ nghĩ như vậy, nhưng str(d) trả về "3.12375239e-24", không phải "0.00000000000000000000000312375239000000000000000000" - Jack