学习Python自定义格式化

158 阅读2分钟

Python f-字符串使用了一种格式化的迷你语言,与老的.format()函数相同。 在冒号之后是如何格式化数值的简短规范。

>>> word = "Hello"
>>> f"{word:/^20}"
'///////Hello////////'
>>> amt = 12345678
>> f"{amt:20,}"
'          12,345,678'

日期时间可以使用strftime语法。

>>> f"{now:%Y-%m on day %d}"
'2022-04 on day 14'

datetime使用与字符串不同的格式化规范的原因是datetime定义了它自己的__format__方法。任何对象都可以定义自己的格式化小语言。 F-strings 和 .format() 将在一个对象上使用 __format__ 方法,并将正在使用的格式化指令传递给它。

>>> class Confused:
...     def __format__(self, fmt):
...         return f"What is {fmt}?"
...
>>> c = Confused()
>>> f"{c:xyz12}"
'What is xyz12?'

当然,__format__可以用于比Confused所做的更有用的格式化......

地理上的纬度和经度通常以几种不同的格式呈现。度;或度和分;或度、分和秒。 然后,这些数字可以有不同的小数位数,有时单位用符号表示。

这里是__format__中对这些可能性的实现。 格式字符串以 "d"、"dm "或 "dms "开头,表示基本格式。 小数点的位数可以用".N "来指定。 最后,可以添加符号,可以是普通的,也可以是花哨的,通过添加引号或分钟符号。

import dataclasses, re

@dataclasses.dataclass
class LatLong:
    lat: float
    long: float

    def __format__(self, fmt):
        dms, nfmt, opts = re.fullmatch(r"(dm?s?)([.\d]*)([′']?)", fmt).groups()
        formatted = []
        for num in [self.lat, self.long]:
            parts = []
            for ms in dms[1:]:
                parts.append(str(int(num)))
                num = abs((num - int(num)) * 60)
            parts.append(format(num, nfmt + "f"))
            syms = None
            if "'" in opts:
                syms = "°'\""
            elif "′" in opts:
                syms = "°′″"
            if opts:
                parts = [p + s for p, s in zip(parts, syms)]
            formatted.append(" ".join(parts))
        joined = ", ".join(formatted)
        return joined
>>> where = LatLong(42.359764937, -71.092068768)
>>> print(f"Location: {where:d'}")
Location: 42.359765°, -71.092069°
>>> print(f"Location: {where:d.4}")
Location: 42.3598, -71.0921
>>> print(f"Location: {where:dm'}")
Location: 42° 21.585896', -71° 5.524126'
>>> print(f"Location: {where:dms.4'}")
Location: 42° 21' 35.1538", -71° 5' 31.4476"
>>> print(f"Location: {where:dms.4}")
Location: 42 21 35.1538, -71 5 31.4476
>>> print(f"Location: {where:dms.6′}")
Location: 42° 21′ 35.153773″, -71° 5′ 31.447565″
>>> print("There: {:dms.6′}".format(where))
There: 42° 21′ 35.153773″, -71° 5′ 31.447565″
>>> print(format(where, "dms.6′"))
42° 21′ 35.153773″, -71° 5′ 31.447565″

这个实现没有正确处理错误,但显示了基本的想法。 另外,纬度/长度通常用N/S E/W而不是正负值显示。