Python format() 语法详解 + HackerRank String Formatting 笔记和题解
Python format() 的语法详解,以及如何用 Python format() 做 HackerRank String Formatting。
Table of Contents
背景
这篇笔记是在我做到 HackerRank 的 String Formatting 时写下的,这道题如果你会用 Python format 打出各种进制(dec, oct, hex, bin)并调整格式的话会简单很多,这篇文章也会详细讲解一下 format() 的用法。
format 的用法
基本用法
print("Hello I am {} and he is {}!".format('Tom','Jerry'))
# 输出:Hello I am Tom and he is Jerry!
在 string 中间直接放 "{}",会和后面 format 里的 arguments 一一对应。
Positional Arguments
我们也可以在 "{}" 中放入数字,来达到我们要的对应效果,这种叫做 positional arguments:
print("Hello I am {1} and he is {0}!".format('Tom','Jerry'))
# 输出:Hello I am Jerry and he is Tom!
Keyword Arguments
当然也可以通过名字来两者当然可以合起来用:,这种叫做 keyword arguments:
print("Hello I am {cat} and he is {mouse}!".format(cat='Tom',mouse='Jerry'))
# 输出:Hello I am Tom and he is Jerry!
Positional and Keyword Arguments
两者当然可以合起来用:
print("Hello I am {0} and he is {mouse}!".format('Tom',mouse='Jerry'))
# 输出:Hello I am Tom and he is Jerry!
format 里面也可以放一个 dictionary:
scores = {'Jerry':98, "Tom":99, "Tuffy":100}
height = {'Jerry':1, "Tom":4, "Tuffy":0.5}
print("Tom's score is {0[Tom]}. Tuffy's height is {1[Tuffy]}!".format(scores,height))
# 输出:Tom's score is 99. Tuffy's height is 0.5!
可以看到这里 "{}" 的 0 和 1 就分别对应了 format 里面的 scores 和 height,再利用 "[]" 对 dictionary 的 key 进行索引。
Format String Syntax
了解了以上的基本操作后,我们再来深入地看一下 format() 的语法。首先,"{}" 里面的东西我们一般叫做 replacement fields,于是就有了下面的语法定义:
replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"
这里的 field name 就是指的我们上面提及的 keyword arguments 和 positional arguments 的位置,而中间的 ["!" conversion] 则告诉我们,其实可以对数据做一个转换,而现在支持的转换有 !s,!r,!a 三种:
- !s 对应 str()
- !r 对应 repr()
- !a 对应 ascii()
比如:
s = '''Hello
Hey'''
print("{!a}".format(s))
# 输出:'Hello\nHey'
Format Specification
而后面的 format_spec 就是我们最常见到的,类似 "{0:2d}" 冒号后面的部分。
format_spec 的语法定义是这样的:
format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]
[sign]
我们先说 [sign] 的部分,sign 的语法定义:
sign ::= "+" | "-" | " "
每个选项及其对应的含义:
Option | Meaning |
---|---|
+ | 正数和负数都需要在前面加 sign (+/-) |
- | 只给负数加 sign (-) |
space | 正数前加空格,负数前加负号 (-) |
举例:
print("{:+} {:+}".format(3, -3))
print("{:-} {:-}".format(3, -3))
print("{: } {: }".format(3, -3))
输出:
+3 -3
3 -3
3 -3
[[fill]align]
接下来我们先看 [[fill]align] 的位置,fill 和 align 的语法定义:
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
align 的每个选项及其对应的含义:
Option | Meaning |
---|---|
< | 向左对齐 |
> | 向右对齐 |
= | 强制将填充放置在 sign(如果有)之后但在数字之前。这用于以“+000000120”形式打印字段。此对齐选项仅对数字类型有效。当'0'紧接在 [width] 之前时,它成为默认值。 |
^ | 居中 |
举例:
print("{:>10}".format('12345'))
print("{:*^10}".format('12345'))
print("{:*=+10}".format(12345))
输出:
12345
**12345***
+****12345
[type]
type 的语法定义:
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
type 在这里可以用来决定 data 的表现形式,比如:
Type | Meaning |
---|---|
'd' | Decimal(十进制) |
'b' | Binary(二进制) |
'o' | Octal(八进制) |
'x' | Hex(十六进制) |
举例:
print("Decimal: {0:d} Binary: {0:b} Octal: {0:o} Hex: {0:x}".format(15))
输出:
Decimal: 15 Binary: 1111 Octal: 17 Hex: f
更多的 types 可以再看参考的 Format String Syntax。
[#]
# 号在这里是用来做一个 alternate form 的 conversion,只对 integer,float 和 complex 有用。比如我们要打二进制的数字,想要前面跟个"0b",就可以加 # 号。
integer 举例:
print("Decimal: {0:#d} Binary: {0:#b} Octal: {0:#o} Hex: {0:#x} Hex_Upper: {0:#X}".format(15))
输出:
Decimal: 15 Binary: 0b1111 Octal: 0o17 Hex: 0xf Hex_Upper: 0XF
对 float 和 complex 的效果则是,不管怎样都会给它加个小数点,即使后边没有 following digits。
举例:
print("{:.0f} {}".format(3, 3j+1))
print("{:#.0f} {:#}".format(3, 3j+1))
输出:
3 (1+3j)
3. (1.+3.j)
[0]
当没有给出明确的 alignment 时,在 [width] 前面加个 ‘0’ 则表示强制在 sign(如有)和数字之间加 0。
比如:
print("{:+06}".format(3))
输出:
+00003
这里的 6 是 width,效果其实相当于前面的 align 用 ‘0’ 填充然后用 ‘=’ 进行 align:
print("{:0=+6}".format(3))
[grouping_option]
grouping_option 的语法定义:
grouping_option ::= "_" | ","
grouping_option 其实就两种,一种是 ‘,’,一种是 ‘_’。
‘,’ 其实就是千位分隔符:
print("{:,}".format(1000000000))
输出:
1,000,000,000
而 ‘_’ 对于 decimal 和 float 来说是千位分隔符:
print("{:_d}".format(1000000000))
print("{:_f}".format(99999.9999))
输出:
1_000_000_000
99_999.999900
而对于 integer 中的 ‘b’,’o’,’x’,和 ‘X’ 则是每 4 位分隔一次:
# 2^20 - 1 = 1048575
print("{:_b}".format(1048575)) #二进制
print("{:_o}".format(1048575)) #八进制
print("{:_x}".format(1048575)) #十六进制
print("{:_X}".format(1048575)) #十六进制加大写
输出:
1111_1111_1111_1111_1111
377_7777
f_ffff
F_FFFF
[.precision]
precision 的语法定义:
precision ::= digit+
precision 相信每个人都用过,类似 {.2f} 就是保留两个小数点,而如果用在非数字类型的 type 上,就是保留多少 characters:
print("{:.3f}".format(3.1415926))
print("{:.3}".format("Hello World"))
输出:
3.142
Hel
HackerRank String Formatting
写了那么多终于可以写题解了。。。
题目
题目给定一个数字 n,$1 \leq n \leq 99$,打印出 $1$ 到 $n$ 的 Decimal, Octal, Hexadecimal, Binary 形式,以 binary 的长度作为 width 进行以下 format。
输入:
17
输出:
1 1 1 1
2 2 2 10
3 3 3 11
4 4 4 100
5 5 5 101
6 6 6 110
7 7 7 111
8 10 8 1000
9 11 9 1001
10 12 A 1010
11 13 B 1011
12 14 C 1100
13 15 D 1101
14 16 E 1110
15 17 F 1111
16 20 10 10000
17 21 11 10001
用 rjust() 做
这道题我一开始是用 rjust 做的,但因为是 python 3,需要考虑到要在width 的基础上多打一个空格,比如最后一行 10 和 10001 之间还有多一个空格。因为在 python 2 里面,如果你用 print i, 会自动补一个空格,比如:
for i in xrange(10):
print i,
# 这是 python 2
# 输出:1 2 3 4 5 6 7 8 9
题解
def print_formatted(number):
width = len(bin(number)[2:])
for i in range(1,number+1):
bin_num = str(bin(i))[2:]
oct_num = str(oct(i))[2:]
hex_num = str(hex(i))[2:]
print(str(i).rjust(width)+" "+ oct_num.rjust(width)+" "+hex_num.rjust(width).upper()+" "+bin_num.rjust(width))
用 format() 做
用 format() 做这题就简单很多了。
题解
def print_formatted(number):
width = len(bin(number)[2:])
for i in range(1, number+1):
print("{0: >{width}} {0: >{width}o} {0: >{width}X} {0: >{width}b}".format(i,width=width))
参考
然的博客 Newsletter
Join the newsletter to receive the latest updates in your inbox.