Python中的format方法
内置的 format()
函数和 str.format()
方法把各个类型的格式化方式委托给相应的 .__format__(format_spec)
方法。format_spec
是格式说明符,它是:
format(my_obj,
格式说明符)
的第二个参数,或者str.format()
方法的格式字符串,{}
里代换字段中冒号后面的部分
格式说明符可以用在format函数中,或者用在str.format方法中。并且在str.format方法中,只表示{}中冒号后面的部分。
实例:
>>> brl = 1/2.43 # BRL到USD的货币兑换比价
>>> brl
0.4115226337448559
>>> format(brl, '0.4f')
'0.4115'
>>> '1 BRL = {rate:0.2f} USD'.format(rate=brl)
'1 BRL = 0.41 USD'
以上代码的0.4f,0.2f就是格式说明符。
在《流畅度Python》中,作者这样建议:
如果你对format()
和str.format()
都感到陌生,根据我的教学经验,最好先学format()
函数,因为它只使用格式规范微语言。学会这些表示法之后,再阅读格式字符串句法(“Format String Syntax”),学习str.format()
方法使用的{:}
代换字段表示法(包含转换标志!s
、!r
和!a
)。
格式化微语言
格式说明符使用的表示法叫格式规范微语言。
format_spec字段包含值应如何呈现的规格描述,例如字段宽度、对齐、填充、小数精度等细节信息。 每种值类型可以定义自己的“格式化迷你语言”或对format_spec的解读方式。我的理解格式化微语言就像是对字符串进行打扮一样。
格式化微语言的语法如下:
format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= digit+
grouping_option ::= "_" | ","
precision ::= digit+
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
举例如下:
align字段+fill字段
如果指定了一个有效的align值,则可以在该值前面加一个fill字符,它可以为任意字符,如果省略则默认为空格符。
'{:<30}'.format('left aligned')
'left aligned '
'{:>30}'.format('right aligned')
' right aligned'
'{:^30}'.format('centered')
' centered '
'{:*^30}'.format('centered') # use '*' as a fill char
'***********centered***********'
sign 字段:
该选项仅对数字类型有效,可以是以下之一:‘+’、‘-’、‘space’
>>> '{:+f}; {:+f}'.format(3.14, -3.14) # show it always
'+3.140000; -3.140000'
>>> '{: f}; {: f}'.format(3.14, -3.14) # show a space for positive numbers
' 3.140000; -3.140000'
>>> '{:-f}; {:-f}'.format(3.14, -3.14) # show only the minus -- same as '{:f}; {:f}'
'3.140000; -3.140000'
'#'
选项:
对于整数类型,当使用二进制、八进制或十六进制输出时,此选项会为输出值分别添加相应的'0b'
,'0o'
,'0x'
或'0X'
前缀。
整数实例:
>>> # format also supports binary numbers
>>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)
'int: 42; hex: 2a; oct: 52; bin: 101010'
>>> # with 0x, 0o, or 0b as prefix:
>>> "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)
'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010'
这是当 '0' 紧接在字段宽度之前时的默认选项。
grouping_option字段:
'_'
选项表示对浮点表示类型和整数表示类型'd'
使用下划线作为千位分隔符。 对于整数表示类型'b'
,'o'
,'x'
和'X'
,将为每 4 个数位插入一个下划线。 对于其他表示类型指定此选项则将导致错误。
','
选项表示使用逗号作为千位分隔符。
'{:,}'.format(1234567890)
'1,234,567,890'
'{:_}'.format(1234567890)
'1_234_567_890'
width字段与0字段:
width是一个定义最小总字段宽度的十进制整数,包括任何前缀、分隔符和其他格式化字符。 如果未指定,则字段宽度将由内容确定。
当未显式给出对齐方式时,在width字段前加一个零 ('0'
) 字段将为数字类型启用感知正负号的零填充。 这相当于设置fill字符为'0'
且alignment类型为'='
。
'{:030}'.format(1234567890)
'000000000000000000001234567890'
'{:030}'.format(-1234567890)
'-00000000000000000001234567890'
precision字段:
precision是一个十进制数字,表示对于以'f'
and'F'
格式化的浮点数值要在小数点后显示多少个数位,或者对于以'g'
或'G'
格式化的浮点数值要在小数点前后共显示多少个数位。 对于非数字类型,该字段表示最大字段大小 —— 换句话说就是要使用多少个来自字段内容的字符。 对于整数值则不允许使用precision。
'{:.2f}'.format(3.1415926)
'3.14'
'{:.2g}'.format(3.1415926)
'3.1'
'{:.2}'.format('xiao tong xue')
'xi'
type字段:
格式字符串句法
替换字段句法:
replacement_field ::= "{" [field_name] ["!" conversion] [":" format_spec] "}"
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
arg_name ::= [identifier | digit+]
attribute_name ::= identifier
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
conversion ::= "r" | "s" | "a"
format_spec ::= <described in the next section>
其中关于format_spec字段已经在上文介绍完毕,下面介绍前面两个字段:
field_name字段:
field_name本身以一个数字或关键字arg_name打头。
如果为数字,则它指向一个位置参数,而如果为关键字,则它指向一个命名关键字参数。 如果格式字符串中的数字 arg_names 为 0, 1, 2, ... 的序列,它们可以全部省略(而非部分省略),数字 0, 1, 2, ... 将会按顺序自动插入。
由于arg_name不使用引号分隔,因此无法在格式字符串中指定任意的字典键 (例如字符串'10'
或':-]'
)。
arg_name之后可以带上任意数量的索引或属性表达式。'.name'
形式的表达式会使用getattr()
选择命名属性,而'[index]'
形式的表达式会使用__getitem__()
执行索引查找。
实例:
"First, thou shalt count to {0}" # References first positional argument
"Bring me a {}" # Implicitly references the first positional argument
"From {} to {}" # Same as "From {0} to {1}"
"My quest is {name}" # References keyword argument 'name'
"Weight in tons {0.weight}" # 'weight' attribute of first positional arg
"Units destroyed: {players[0]}" # First element of keyword argument 'players'.
按名称访问参数:
>>> 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24N', longitude='-115.81W')
'Coordinates: 37.24N, -115.81W'
>>> coord = {'latitude': '37.24N', 'longitude': '-115.81W'}
>>> 'Coordinates: {latitude}, {longitude}'.format(**coord)
'Coordinates: 37.24N, -115.81W'
访问参数的属性:
>>> c = 3-5j
>>> ('The complex number {0} is formed from the real part {0.real} '
... 'and the imaginary part {0.imag}.').format(c)
'The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.'
>>> class Point:
... def __init__(self, x, y):
... self.x, self.y = x, y
... def __str__(self):
... return 'Point({self.x}, {self.y})'.format(self=self)
...
>>> str(Point(4, 2))
'Point(4, 2)'
访问参数的项:
>>> coord = (3, 5)
>>> 'X: {0[0]}; Y: {0[1]}'.format(coord)
'X: 3; Y: 5'
conversion字段:
使用 conversion 字段在格式化之前进行类型强制转换。 通常,格式化值的工作由值本身的__format__()
方法来完成。 但是,在某些情况下最好强制将类型格式化为一个字符串,覆盖其本身的格式化定义。 通过在调用__format__()
之前将值转换为字符串,可以绕过正常的格式化逻辑。
目前支持的转换旗标有三种:'!s'
会对值调用str()
,'!r'
调用repr()
而'!a'
则调用ascii()
。
"Harold's a clever {0!s}" # Calls str() on the argument first
"Bring out the holy {name!r}" # Calls repr() on the argument first
"More {!a}" # Calls ascii() on the argument first
>>> "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
"repr() shows quotes: 'test1'; str() doesn't: test2"
实现自定义的格式化微语言
假设存在一个类Vector2d,它的类定义如下:
from array import array
import math
class Vector2d:
typecode = 'd'
def __init__(self, x, y):
self.__x = float(x)
self.__y = float(y)
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def __iter__(self):
return (i for i in (self.x, self.y))
def __repr__(self):
class_name = type(self).__name__
return '{}({!r}, {!r})'.format(class_name, *self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes(ord(self.typecode)) + bytes(array(self.typecode, self)))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __hash__(self):
return hash(self.x) ^ hash(self.y)
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
现在需要自定义他的格式化微语言,需求如下:
>>> v1 = Vector2d(3, 4)
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
那么需要重写其_format_方法,代码如下:
def __format__(self, fmt_spec=''):
components = (format(c, fmt_spec) for c in self)
return '({}, {})'.format(*components)
❶ 使用内置的 format
函数把 fmt_spec
应用到向量的各个分量上,构建一个可迭代的格式化字符串。
❷ 把格式化字符串代入公式 '(x, y)'
中。
但是以上只是将常规的format方法应用到每个元素上,如果需要自定义格式化微语言,实现以下的功能:
即,如果格式说明符以'p'
结尾,那么在极坐标中显示向量,即<r, θ >
,其中r
是模,θ
(西塔)是弧度;其他部分('p'
之前的部分)像往常那样解释。
>>> format(Vector2d(1, 1), 'p')
'<1.4142135623730951, 0.7853981633974483>'
>>> format(Vector2d(1, 1), '.3ep')
'<1.414e+00, 7.854e-01>'
>>> format(Vector2d(1, 1), '0.5fp')
'<1.41421, 0.78540>'
需要使用到atan2函数:
atan2 方法返回一个 -pi 到 pi 之间的数值,表示点 (x, y) 对应的偏移角度。这是一个逆时针角度,以弧度为单位,正X轴和点 (x, y) 与原点连线之间。函数接受的参数:先传递 y 坐标,然后是 x 坐标。
定义一个计算弧度的函数:
def angle(self):
return math.atan2(self.y, self.x)
完成Vector2d类定义:
# -*- coding:utf-8 -*-
"""
作者:tjygg
日期:2021年月18日
"""
from array import array
import math
class Vector2d:
typecode = 'd'
def __init__(self, x, y):
self.__x = float(x)
self.__y = float(y)
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def __iter__(self):
return (i for i in (self.x, self.y))
def __repr__(self):
class_name = type(self).__name__
return '{}({!r}, {!r})'.format(class_name, *self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return (bytes(ord(self.typecode)) + bytes(array(self.typecode, self)))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __hash__(self):
return hash(self.x) ^ hash(self.y)
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def angle(self):
return math.atan2(self.y, self.x)
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'): #1
fmt_spec = fmt_spec[:-1] #2
coords = (self.__abs__(), self.angle()) #3
outer_fmt = '<{}, {}>' #4
else:
coords = self %5
outer_fmt = '({}, {})' %6
component = (format(c, fmt_spec) for c in coords) #7
return outer_fmt.format(*component) #8
print(format(Vector2d(1, 1), 'p'))
print(format(Vector2d(1, 1), '.3ep'))
print(format(Vector2d(1, 1), '0.5fp'))
❶ 如果格式代码以 'p'
结尾,使用极坐标。
❷ 从 fmt_spec
中删除 'p'
后缀。
❸ 构建一个元组,表示极坐标:(magnitude, angle)
。
❹ 把外层格式设为一对尖括号。
❺ 如果不以 'p'
结尾,使用 self
的 x
和 y
分量构建直角坐标。
❻ 把外层格式设为一对圆括号。
❼ 使用各个分量生成可迭代的对象,构成格式化字符串。
❽ 把格式化字符串代入外层格式。