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

参考

  1. Python Document 7.1.2 The String format() Method
  2. Format String Syntax
HackerRankPython简单难度

Comments