การจัดรูปแบบสตริง:% กับ. format


1349

Python 2.6 แนะนำstr.format()วิธีการที่มีไวยากรณ์แตกต่างกันเล็กน้อยจาก%ผู้ให้บริการที่มีอยู่ ไหนดีกว่าและสำหรับสถานการณ์อะไร

  1. ต่อไปนี้ใช้แต่ละวิธีและมีผลลัพธ์เหมือนกันดังนั้นความแตกต่างคืออะไร

    #!/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. นอกจากนี้การจัดรูปแบบสตริงเกิดขึ้นใน Python เมื่อใด ตัวอย่างเช่นหากระดับการบันทึกของฉันถูกตั้งค่าเป็นสูงฉันจะยังคงได้รับผลกระทบจากการดำเนินการต่อไปนี้%หรือไม่ และถ้าเป็นเช่นนั้นมีวิธีที่จะหลีกเลี่ยงปัญหานี้หรือไม่?

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

2
คล้ายกับstackoverflow.com/questions/3691975/…
carl

2
สำหรับผู้เริ่มต้น: นี่คือบทช่วยสอนที่ดีมากที่สอนทั้งสองสไตล์ โดยส่วนตัวแล้วฉันใช้%สไตล์เก่าๆ บ่อยขึ้นเพราะถ้าคุณไม่ต้องการความสามารถที่เพิ่มขึ้นของformat()สไตล์%สไตล์นั้นมักจะสะดวกกว่ามาก
Lutz Prechelt

2
สำหรับการอ้างอิง: งูหลามเอกสาร 3 สำหรับรุ่นใหม่format()สไตล์การจัดรูปแบบและเก่า%สไตล์การจัดรูปแบบชั่น
Lutz Prechelt


1
หากต้องการตอบคำถามที่สองของคุณตั้งแต่ 3.2 คุณสามารถใช้รูปแบบ {} หากคุณใช้ฟอร์แมตเตอร์แบบกำหนดเอง (ดูdocs.python.org/3/library/logging.html#logging.Formatter )
yanjost

คำตอบ:


953

เพื่อตอบคำถามแรกของคุณ ... .formatดูเหมือนจะซับซ้อนกว่านี้ในหลาย ๆ ทาง สิ่งที่น่ารำคาญก็%คือมันสามารถใช้ตัวแปรหรือทูเปิลได้ คุณคิดว่าสิ่งต่อไปนี้จะได้ผลเสมอ:

"hi there %s" % name

แต่ถ้าnameเกิดขึ้นจะเป็นก็จะโยน(1, 2, 3) TypeErrorหากต้องการรับประกันว่าจะพิมพ์ออกมาเสมอคุณต้องทำ

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

ซึ่งน่าเกลียดเพียง .formatไม่มีปัญหาเหล่านั้น นอกจากนี้ในตัวอย่างที่สองที่คุณให้.formatตัวอย่างนั้นดูสะอาดตากว่ามาก

ทำไมคุณไม่ใช้มัน?

  • ไม่ทราบเกี่ยวกับมัน (ฉันก่อนที่จะอ่านนี้)
  • ต้องเข้ากันได้กับ Python 2.5

เพื่อตอบคำถามที่สองของคุณการจัดรูปแบบสตริงเกิดขึ้นในเวลาเดียวกันกับการดำเนินการอื่น ๆ - เมื่อนิพจน์การจัดรูปแบบสตริงถูกประเมิน และ Python ไม่เป็นภาษาขี้เกียจประเมินการแสดงออกก่อนที่จะเรียกฟังก์ชั่นดังนั้นในของคุณlog.debugตัวอย่างเช่นการแสดงออก"some debug info: %s"%some_infoแรกจะประเมินเช่นแล้วว่าสตริงจะถูกส่งผ่านไปยัง"some debug info: roflcopters are active"log.debug()


113
สิ่งที่เกี่ยวกับ"%(a)s, %(a)s" % {'a':'test'}
เท็ด

128
โปรดทราบว่าคุณจะเสียเวลาlog.debug("something: %s" % x)แต่ไม่ใช่สำหรับlog.debug("something: %s", x) การจัดรูปแบบสตริงจะได้รับการจัดการในวิธีการและคุณจะไม่ได้รับประสิทธิภาพการทำงานหากไม่มีการบันทึก เช่นเคยงูใหญ่คาดหวังความต้องการของคุณ =)
darkfeline

63
เท็ด: '{0}, {0}'.format('test')ที่สับเลวร้ายยิ่งมองหาที่จะทำเช่นเดียวกับ
แกะบินได้

19
จุดคือ: อาร์กิวเมนต์หนึ่งที่เกิดขึ้นซ้ำที่ไวยากรณ์ใหม่อนุญาตให้เรียงลำดับรายการเป็นจุดที่สงสัย: คุณสามารถทำเช่นเดียวกันกับไวยากรณ์เก่า คนส่วนใหญ่ไม่ทราบว่าสิ่งนี้มีการกำหนดไว้แล้วใน Ansi C99 Std! ตรวจสอบสำเนาล่าสุดman sprintfและเรียนรู้เกี่ยวกับ$สัญลักษณ์ใน%ตัวยึดตำแหน่ง
cfi

29
@cfi: ถ้าคุณหมายถึงprintf("%2$d", 1, 3)การพิมพ์ "3" ที่ระบุใน POSIX ไม่ใช่ C99 หน้าคนที่คุณอ้างถึงบันทึกย่อ "มาตรฐาน C99 ไม่รวมสไตล์โดยใช้ '$' ... "
Thanatos

307

สิ่งที่ตัวดำเนินการ modulo (%) ไม่สามารถทำได้ Afaik:

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

ผลลัพธ์

12 22222 45 22222 103 22222 6 22222

มีประโยชน์มาก.

อีกจุดหนึ่ง: format()เป็นฟังก์ชันสามารถใช้เป็นอาร์กิวเมนต์ในฟังก์ชันอื่นได้:

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

ผลลัพธ์ใน:

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

17
คุณสามารถใช้การจัดรูปแบบแบบเก่าmapได้อย่างง่ายดายเหมือนกับการจัดรูปแบบ map('some_format_string_%s'.__mod__, some_iterable)
agf

3
@cfi: โปรดพิสูจน์ว่าคุณถูกต้องโดยเขียนใหม่ตัวอย่างข้างต้นใน C99
MarcH

9
@MarcH: printf("%2$s %1$s\n", "One", "Two");รวบรวมกับผลลัพธ์คือgcc -std=c99 test.c -o test Two Oneแต่ฉันยืนแก้ไข: จริง ๆ แล้วมันเป็นส่วนขยาย POSIXและไม่ใช่ C. ฉันไม่พบมันอีกครั้งในมาตรฐาน C / C ++ ที่ฉันคิดว่าฉันเห็นมัน รหัสทำงานได้แม้กับการตั้งค่าสถานะ 'c90' std หน้าคนsprintf นี่ไม่ได้แสดงรายการ แต่อนุญาตให้ใช้ libs ในการติดตั้ง superset อาร์กิวเมนต์เดิมของฉันยังคงใช้ได้โดยแทนที่Cด้วยPosix
cfi

8
ความคิดเห็นแรกของฉันที่นี่ไม่ได้ใช้กับคำตอบนี้ ฉันเสียใจที่ใช้ถ้อยคำ ใน Python เราไม่สามารถใช้โอเปอเรเตอร์มอดูโล%เพื่อจัดเรียงตัวยึดตำแหน่งใหม่ ฉันยังคงไม่ต้องการลบความคิดเห็นแรกเพื่อความสอดคล้องของความคิดเห็นที่นี่ ฉันขอโทษที่ระบายความโกรธที่นี่ มันตรงกับคำสั่งที่ทำบ่อยว่าไวยากรณ์เก่าต่อ se จะไม่อนุญาตให้นี้ แทนที่จะสร้างซินแท็กซ์ใหม่ทั้งหมดเราสามารถแนะนำ std Posix extensions เรามีได้ทั้งคู่
cfi

17
'modulo' หมายถึงโอเปอเรเตอร์ที่ประเมินค่าส่วนที่เหลือหลังจากการหาร ในกรณีนี้เครื่องหมายเปอร์เซ็นต์ไม่ใช่ตัวดำเนินการโมดูโล
Octopus

148

สมมติว่าคุณกำลังใช้loggingโมดูลPython คุณสามารถส่งอาร์กิวเมนต์การจัดรูปแบบสตริงเป็นอาร์กิวเมนต์ไปยัง.debug()วิธีการแทนที่จะทำรูปแบบด้วยตัวคุณเอง:

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

ซึ่งหลีกเลี่ยงการจัดรูปแบบเว้นแต่คนตัดไม้บันทึกสิ่งจริง


10
นี่เป็นข้อมูลที่มีประโยชน์ที่ฉันเพิ่งเรียนรู้ตอนนี้ มันน่าเสียดายที่มันไม่มีคำถามของตัวเองเพราะมันดูเหมือนแยกจากคำถามหลัก สงสาร OP ไม่ได้แยกคำถามของเขาออกเป็นสองคำถาม
snth

12
คุณสามารถใช้การจัดรูปแบบ dict ดังนี้: log.debug("some debug info: %(this)s and %(that)s", dict(this='Tom', that='Jerry')) อย่างไรก็ตามคุณไม่สามารถใช้.format()ไวยากรณ์สไตล์ใหม่ได้ที่นี่ไม่ได้แม้แต่ใน Python 3.3 ซึ่งเป็นความอัปยศ
Cito

15
@Cito: ดูสิ่งนี้: plumberjack.blogspot.co.uk/2010/10//
Vinay Sajip

26
ประโยชน์หลักของสิ่งนี้ไม่ใช่ประสิทธิภาพ (การดำเนินการแก้ไขสตริงจะเป็นการเปรียบเทียบอย่างรวดเร็วกับสิ่งที่คุณทำกับเอาต์พุตจากการบันทึกเช่นการแสดงในเทอร์มินัลการบันทึกลงดิสก์) นั่นคือถ้าคุณมีตัวรวบรวมการบันทึก สามารถบอกคุณได้ว่า "คุณได้รับข้อความแสดงข้อผิดพลาด 12 ครั้ง" แม้ว่าพวกเขาจะมีค่า 'some_info' ที่แตกต่างกันก็ตาม หากการจัดรูปแบบสตริงเสร็จสิ้นก่อนส่งผ่านสตริงไปที่ log.debug นี่จะเป็นไปไม่ได้ ผู้รวบรวมสามารถพูดว่า "คุณมีข้อความบันทึกที่แตกต่างกัน 12 ข้อความเท่านั้น"
Jonathan Hartley

7
หากคุณกังวลเกี่ยวกับประสิทธิภาพการทำงานให้ใช้ตัวอักษร dict {} ไวยากรณ์แทนการสร้างอินสแตนซ์ของคลาส dict (): doughellmann.com/2012/11/…
trojjer

119

ตั้งแต่ Python 3.6 (2016) คุณสามารถใช้f-stringsเพื่อทดแทนตัวแปร:

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

หมายเหตุf"คำนำหน้า หากคุณลองทำสิ่งนี้ใน Python 3.5 หรือSyntaxErrorเก่ากว่าคุณจะได้รับ

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


1
สิ่งนี้ไม่ตอบคำถาม อีกคำตอบหนึ่งที่กล่าวถึง f-strings อย่างน้อยก็พูดถึงประสิทธิภาพ: stackoverflow.com/a/51167833/7851470
Georgy

60

PEP 3101เสนอการแทนที่%โอเปอเรเตอร์ด้วยการจัดรูปแบบสตริงขั้นสูงใหม่ใน Python 3 ซึ่งจะเป็นค่าเริ่มต้น


14
ไม่จริง: "ความเข้ากันได้ย้อนหลังสามารถรักษาได้โดยปล่อยกลไกที่มีอยู่ให้เข้าที่"; แน่นอน.formatจะไม่แทนที่ %การจัดรูปแบบสตริง
โทเบียส

12
ไม่การวางตำแหน่งของ BrainStorms นั้นเป็นจริง: "ตั้งใจจะใช้แทน '%' ที่มีอยู่เดิม ใบเสนอราคา Tobias หมายถึงทั้งสองระบบจะอยู่ร่วมกันเป็นระยะ RTFPEP
phobie

54

แต่โปรดระวังตอนนี้ฉันได้ค้นพบปัญหาหนึ่งข้อเมื่อพยายามแทนที่ทั้งหมด%ด้วย.formatรหัสที่มีอยู่: '{}'.format(unicode_string)จะพยายามเข้ารหัส unicode_string และอาจล้มเหลว

เพียงดูบันทึกเซสชันแบบโต้ตอบ Python นี้:

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เป็นเพียงสตริง (เรียกว่า 'byte array' ใน Python3) และuเป็นสตริง Unicode (เรียกว่า 'string' ใน Python3):

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

เมื่อคุณให้วัตถุ Unicode เป็นพารามิเตอร์ให้กับ%โอเปอเรเตอร์มันจะสร้างสตริง Unicode แม้ว่าสตริงเดิมไม่ใช่ 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)

แต่.formatฟังก์ชั่นจะเพิ่ม "UnicodeEncodeError":

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

และมันจะทำงานกับอาร์กิวเมนต์ Unicode ได้ก็ต่อเมื่อสตริงเดิมคือ Unicode

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

หรือถ้าสตริงอาร์กิวเมนต์สามารถแปลงเป็นสตริง (เรียกว่า 'ไบต์อาร์เรย์')


12
มีเพียงเหตุผลที่จะเปลี่ยนรหัสทำงานเว้นแต่คุณสมบัติเพิ่มเติมของใหม่ไม่มีformatวิธีการที่มีความจำเป็นจริงๆ ...
โทเบียส

อย่างเห็นด้วยกับคุณโทเบียส แต่บางครั้งมันจำเป็นเมื่ออัพเกรดเป็นรุ่นใหม่ของงูหลาม
wobmene

2
เช่น AFAIK ไม่เคยมีใครต้องการ ฉันไม่คิดว่าเป็นไปได้ว่าการ%แก้ไขสตริงจะหายไป
โทเบียส

4
ฉันพิจารณา. format () ฟังก์ชันปลอดภัยกว่า% สำหรับสตริง บ่อยครั้งที่ฉันเห็นข้อผิดพลาดเริ่มต้นเช่นนี้หรือ"p1=%s p2=%d" % "abc", 2 "p1=%s p2=%s" % (tuple_p1_p2,)คุณอาจคิดว่ามันเป็นความผิดของ coder แต่ฉันคิดว่ามันเป็นเพียงความผิดปกติของไวยากรณ์ผิดปกติที่ดูดีสำหรับ quicky-scriptie แต่ไม่ดีสำหรับรหัสการผลิต
wobmene

3
แต่ผมไม่ชอบไวยากรณ์ของ .format () ที่ฉันต้องการจะมีความสุขกับเก่าที่ดี%s, ชอบ%02d "p1=%s p2=%02d".format("abc", 2)ฉันตำหนิผู้ที่คิดค้นและอนุมัติการจัดฟันแบบหยิกที่ต้องการให้คุณหลบหนีพวกเขาเช่น{{}}และดูน่าเกลียด imho
wobmene

35

อีกข้อได้เปรียบของ.format(ซึ่งฉันไม่เห็นในคำตอบ): มันสามารถใช้คุณสมบัติของวัตถุ

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'

หรือเป็นอาร์กิวเมนต์ของคำหลัก:

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

นี่เป็นไปไม่ได้%ที่ฉันจะบอกได้


4
'x is {0}, y is {1}'.format(a.x, a.y)ลักษณะนี้ไม่สามารถอ่านได้เกินกว่าที่จำเป็นเมื่อเทียบกับเทียบเท่า ควรใช้เมื่อการa.xดำเนินการมีราคาแพงมาก
dtheodor

13
@dtheodor กับปรับแต่งเพื่อใช้ในการโต้แย้งคำหลักแทนการโต้แย้งตำแหน่ง 'x is {a.x}, y is {a.y}'.format(a=a)... อ่านง่ายกว่าทั้งสองตัวอย่าง
CivFan

1
@CivFan หรือถ้าคุณมีวัตถุมากกว่าหนึ่งอย่าง'x is {a.x}, y is {a.y}'.format(**vars())
Jack

1
โปรดสังเกตสิ่งนี้ด้วยวิธีเดียวกัน: '{foo[bar]}'.format(foo={'bar': 'baz'}).
แอนทอน Pinsard

3
สิ่งนี้มีประโยชน์อย่างมากสำหรับแอปพลิเคชันที่ต้องเผชิญกับลูกค้าโดยที่แอปพลิเคชันของคุณมีชุดตัวเลือกการจัดรูปแบบมาตรฐานพร้อมสตริงรูปแบบที่ผู้ใช้จัดหา ฉันใช้สิ่งนี้ตลอดเวลา ตัวอย่างเช่นไฟล์การกำหนดค่าจะมีคุณสมบัติ "messagestring" ซึ่งผู้ใช้สามารถจัดหาให้กับ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}หรือสิ่งที่พวกเขาต้องการ นี่ไกลกว่าการพยายามเสนอฟังก์ชั่นเดียวกันกับฟอร์แมตเตอร์เดิม มันทำให้สตริงรูปแบบที่ผู้ใช้จัดหาให้มีประสิทธิภาพมากกว่า
Taywee

35

%ให้ประสิทธิภาพที่ดีกว่าformatจากการทดสอบของฉัน

รหัสทดสอบ:

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

ผลลัพธ์:

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

ผลลัพธ์

> format: 0.5864730989560485
> %: 0.013593495357781649

มันดูใน Python2 ความแตกต่างเล็กในขณะที่ Python3 %นั้นเร็วกว่าformatมาก

ขอบคุณ @Chris Cogdon สำหรับตัวอย่างโค้ด

แก้ไข 1:

ทดสอบอีกครั้งใน Python 3.7.2 ในเดือนกรกฎาคม 2019

ผลลัพธ์:

> format: 0.86600608
> %: 0.630180146

มีความแตกต่างไม่มาก ฉันเดาว่า Python กำลังค่อยๆดีขึ้น

แก้ไข 2:

หลังจากมีคนพูดถึง f-string ของ python 3 ในความคิดเห็นฉันได้ทำการทดสอบโค้ดต่อไปนี้ภายใต้ python 3.7.2:

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

ผลลัพธ์:

format: 0.8331376779999999
%: 0.6314778750000001
f-string: 0.766649943

ดูเหมือนว่า F-สตริงยังคงช้ากว่าแต่ดีกว่า%format


42
แต่str.formatจะให้ฟังก์ชันการทำงานมากขึ้น (โดยเฉพาะการจัดรูปแบบเฉพาะประเภทเช่น'{0:%Y-%m-%d}'.format(datetime.datetime.utcnow())) ประสิทธิภาพไม่สามารถเป็นข้อกำหนดที่แท้จริงของงานทั้งหมดได้ ใช้เครื่องมือที่เหมาะสมสำหรับงาน
minhee

36
"การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากของความชั่วร้ายทั้งหมด"หรือดังนั้น Donald Knuth เคยกล่าวไว้ว่า ...
Yatharth Agarwal

22
การผสานกับรูปแบบการจัดรูปแบบที่รู้จักกันดี (ตราบเท่าที่เหมาะสมกับความต้องการซึ่งทำในกรณีส่วนใหญ่) และเร็วกว่าสองเท่าไม่ใช่ "การเพิ่มประสิทธิภาพก่อนวัยอันควร" แต่ก็สมเหตุสมผล BTW %ผู้ประกอบการอนุญาตให้นำprintfความรู้กลับมาใช้ใหม่ การแก้ไขพจนานุกรมเป็นการขยายหลักการที่ง่ายมาก
โทเบียส

5
ฉันเคยเจอสิ่งที่ตรงกันข้ามในสถานการณ์จริง การจัดรูปแบบใหม่นั้นเร็วกว่า คุณสามารถให้รหัสทดสอบที่คุณใช้หรือไม่
David Sanders

8
ดูเหมือนว่าโพสต์ที่สูญเสียไปอย่างจริงจังโดยไม่มีตัวอย่างหรือเหตุผลใด ๆ เพียงอ้างสิทธิ์
kevr

31

ตามที่ฉันค้นพบในวันนี้การจัดรูปแบบสตริงแบบเก่า%ไม่สนับสนุนDecimalโมดูลของ Python สำหรับจุดทศนิยมคงที่และเลขคณิตจุดลอยตัวนอกกรอบ

ตัวอย่าง (โดยใช้ 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))

เอาท์พุท:

0.000000000000000000000000000312375239000000009907464850 0.000000000000000000000000000312375239000000000000000000

อาจมีวิธีแก้ปัญหาอย่างแน่นอน แต่คุณยังอาจลองใช้format()วิธีนี้ในทันที


1
อาจเป็นเพราะการจัดรูปแบบรูปแบบใหม่เรียกstr(d)ก่อนที่จะขยายพารามิเตอร์ในขณะที่การจัดรูปแบบแบบเก่าอาจเรียกfloat(d)ก่อน
David Sanders

3
คุณคิดอย่างนั้น แต่str(d)จะกลับ"3.12375239e-24"มาไม่ใช่"0.00000000000000000000000312375239000000000000000000"
Jack

18

ถ้า python ของคุณ> = 3.6 ฟอร์แมทที่จัดรูปแบบตัวอักษร F คือเพื่อนใหม่ของคุณ

มันง่ายกว่าสะอาดและมีประสิทธิภาพดีกว่า

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)

15

ในฐานะที่เป็นบันทึกย่อด้านข้างคุณไม่จำเป็นต้องใช้ประสิทธิภาพในการจัดรูปแบบรูปแบบใหม่พร้อมการบันทึก คุณสามารถส่งผ่านวัตถุใด ๆ ที่จะlogging.debug, logging.infoฯลฯ ที่ใช้__str__วิธีมายากล เมื่อโมดูลการบันทึกได้ตัดสินใจแล้วว่ามันจะต้องปล่อยวัตถุข้อความของคุณ (ไม่ว่ามันจะเป็นอะไร) มันก็จะเรียกstr(message_object)ก่อนที่จะทำเช่นนั้น ดังนั้นคุณสามารถทำสิ่งนี้:

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

นี่คือทั้งหมดที่อธิบายไว้ในเอกสาร Python 3 ( https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles ) อย่างไรก็ตามมันจะทำงานกับ Python 2.6 ได้เช่นกัน ( https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages )

ข้อดีอย่างหนึ่งของการใช้เทคนิคนี้นอกเหนือจากความจริงที่ว่ามันเป็นรูปแบบที่ไม่เชื่อเรื่องพระเจ้าคือมันช่วยให้ค่าที่ขี้เกียจเช่นฟังก์ชั่นexpensive_funcด้านบน นี้เป็นทางเลือกที่สง่างามมากขึ้นในการให้คำแนะนำที่ถูกกำหนดในเอกสารหลามที่นี่: https://docs.python.org/2.6/library/logging.html#optimization


2
ฉันหวังว่าฉันจะโหวตได้มากกว่านี้ มันช่วยให้การเข้าสู่ระบบformatโดยไม่มีการเข้าชมการทำงาน - ทำได้โดยการเอาชนะ__str__อย่างแม่นยำตามที่loggingได้รับการออกแบบมา - ลดการเรียกฟังก์ชันเป็นอักษรตัวเดียว ( N) ซึ่งให้ความรู้สึกคล้ายกับวิธีมาตรฐานในการกำหนดสตริง - ฟังก์ชั่นการโทร ขอบคุณ! +1
CivFan

2
สิ่งนี้แตกต่างจากการใช้logging.Formatter(style='{')พารามิเตอร์หรือไม่?
davidA

10

สถานการณ์หนึ่งที่%อาจช่วยได้คือเมื่อคุณจัดรูปแบบนิพจน์ regex ตัวอย่างเช่น,

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

IndexErrorยก ในสถานการณ์นี้คุณสามารถใช้:

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

เขียน regex '{type_names} [a-z]{{2}}'เป็นหลีกเลี่ยงนี้ สิ่งนี้มีประโยชน์เมื่อคุณมีสอง regexes โดยที่ใช้เพียงอย่างเดียวโดยไม่มีรูปแบบ แต่การต่อข้อมูลของทั้งสองถูกจัดรูปแบบ


3
'{type_names} [a-z]{{2}}'.format(type_names='triangle|square')หรือการใช้งานเพียงแค่ มันเหมือนกับว่าการพูด.format()สามารถช่วยได้เมื่อใช้สตริงที่มีอักขระเป็นเปอร์เซ็นต์อยู่แล้ว แน่ใจ คุณต้องหลบหนีพวกเขาแล้ว
Alfe

1
@Alfe คุณพูดถูกและนั่นเป็นสาเหตุว่าทำไมคำตอบเริ่มต้นโดย"One situation where % may help is when you are formatting regex expressions."เฉพาะสมมติว่าa=r"[a-z]{2}"เป็น regex อันที่คุณจะใช้ในสองนิพจน์สุดท้ายที่แตกต่างกัน (เช่นc1 = b + aและc2 = a) สมมติว่าc1จำเป็นต้องมีformated (เช่นbต้องมีการจัดรูปแบบรันไทม์) แต่c2ไม่ แล้วคุณจะต้องa=r"[a-z]{2}"สำหรับc2และสำหรับa=r"[a-z]{{2}}" c1.format(...)
Jorge Leitao

7

ฉันจะเพิ่มที่ตั้งแต่รุ่น 3.6 เราสามารถใช้ fstrings ดังต่อไปนี้

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

ซึ่งให้

ฉันชื่อจอห์นสมิ ธ

ทุกอย่างถูกแปลงเป็นสตริง

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

ผลลัพธ์:

mylist = ['foo', 'bar']

คุณสามารถผ่านฟังก์ชั่นเช่นเดียวกับในรูปแบบอื่น ๆ วิธี

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

ยกตัวอย่างเช่น

สวัสดีนี่คือวันที่: 16/04/2018



2

Python 3.6.7 เปรียบเทียบ:

#!/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 __name__ == '__main__':
    main()

เอาท์พุท:

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

3
คุณควรเรียกใช้แต่ละตัวอย่างหลายครั้งการเรียกใช้ครั้งเดียวอาจทำให้เข้าใจผิดเช่นระบบปฏิบัติการอาจไม่ว่างโดยทั่วไปดังนั้นการเรียกใช้โค้ดของคุณจึงล่าช้า ดูเอกสาร: docs.python.org/3/library/timeit.html (อวตารที่ดี Guybrush!)
jake77

1

แต่สิ่งหนึ่งก็คือถ้าคุณมีเครื่องหมายปีกกาแบบซ้อนอยู่ด้วยจะไม่สามารถใช้รูปแบบได้ แต่%จะใช้งานได้

ตัวอย่าง:

>>> '{{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}'
>>> 

2
คุณสามารถทำสิ่งนี้ได้ แต่ฉันเห็นด้วยว่ามันแปลกมาก '{{{0}, {1}}}' รูปแบบ (1, 2)
Sylvan LE DEUNFF

คำหลักแบบปีกกา - ปีกกาซ้อนกันทำงานและค่อนข้างสวย
CivFan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.