《Python面向对象编程指南》——2.2 __format__()方法

简介:

本节书摘来自异步社区《Python面向对象编程指南》一书中的第2章,第2.2节,作者[美]Steven F. Lott, 张心韬 兰亮 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.2 __format__()方法

string.format()和内置的format()函数都使用了__format__()方法。它们都是为了获得给定对象的一个符合要求的字符串表示。

下面是给__format__()传参的两种方式。

  • someobject.__format__(""):当应用程序中出现format(someobject)或者"{0}".format(someobject)时,会默认以这种方式调用__format__()。在这些情况下,会传递一个空字符串,__format__()的返回值会以默认格式表示。
  • someobject.__format__(specification):当应用程序中出现format (someobject, specification)或者"{0:specification}".format (someobject)"时,会默认以这种方式调用__format__()。

注意,"{0!r}".format()和"{0!s}".format()并不会调用__format__()方法。它们会直接调用__repr__()或者__str__()。

当specification是""时,一种合理的返回值是return str(self),这为各种对象的字符串表示形式提供了明确的一致性。

在一个格式化字符串中,":"之后的文本都属于格式规范。当我们写"{0:06.4f}"时,06.4f是应用在项目0上的格式规范。

Python标准库的6.1.3.1节定义了一个复杂的数值规范,它是一个包括9个部分的字符串。这就是格式规范的基本语法,它的语法如下。

[[fill]align][sign][#][0][width][,][.precision][type]

这些规范的正则表示如下。

re.compile(
r"(?P<fill_align>.?[\<\>=\^])?"
"(?P<sign>[-+ ])?"
"(?P<alt>#)?"
"(?P<padding>0)?"
"(?P<width>\d*)"
"(?P<comma>,)?"
"(?P<precision>\.\d*)?"
"(?P<type>[bcdeEfFgGnosxX%])?" )

这个正则表达式将规范分解为8个部分。第1部分同时包括了原本规范中的fill和alignment字段。我们可以利用它们定义我们的类中的数值类型的格式。

但是,Python格式规范的语法有可能不能很好地应用到我们之前定义的类上。所以,我们可能需要定义我们自己的规范化语法,并且使用我们自己的__format__()方法来处理它。如果我们定义的是数值类型,那么我们应该使用Python中内建的语法。但是,对于其他类型,没有理由坚持使用预定义的语法。

例如,下面是我们自定义的一个微型语言,用%r来表示rank,用%s来表示suit,用%代替%%,所有其他的文本保持不变。

我们可以用下面的格式化方法扩展Card类。

def __format__( self, format_spec ):
   if format_spec == "":
     return str(self)
   rs= format_spec.replace("%r",self.rank).replace("%s",self.suit)
   rs= rs.replace("%%","%")
   return rs

方法签名中,需要一个format_spec作为格式规范参数。如果没有提供这个参数,那么就会使用str()函数来返回结果。如果提供了格式规范参数,就会用rank、suit和%字符替换规范中对应的部分,来生成最后的结果。

这允许我们使用下面的方法来格式化牌。

print( "Dealer Has {0:%r of %s}".format( hand.dealer_card) )

其中,("%r of %s")作为格式化参数传入__format__()方法。通过这种方式,我们能够为描述自定义对象提供统一的接口。

或者,我们可以用下面的方法:

default_format= "some specification"
def __str__( self ):
   return self.__format__( self.default_format )
def __format__( self, format_spec ):
   if format_spec == "": format_spec = self.default_format
   # process the format specification.

这种方法的优点是把所有与字符串表示相关的逻辑放在__format__()方法中,而不是分别写在__format__()和__str__()里。但是,这样做有一个缺点,因为并非每次都需要实现__format__()方法,但是我们总是需要实现__str__()。

2.2.1 内嵌格式规范

string.format()方法可以处理{}中内嵌的实例,替换其中的关键字,生成新的格式规范。这种替换是为了生成最后传入__format__()中的格式化字符串。通过使用这种内嵌的替换,我们可以使用一种更加简单的带参数的更加通用的格式规范,而不是使用相对复杂的数值格式。

下面是使用内嵌格式规范的一个例子,它让format参数中的width更容易改变:

width=6
for hand,count in statistics.items():
   print( "{hand}{count:{width}d}".format(hand=hand,count=count,width= width) )

我们定义了一个通用的格式,"{hand}{count:{width}d}",它需要一个width参数,才算是一个正确的格式规范。

通过width=参数提供的值会被用来替换{width}。替换完成后,完整的格式化字符串会作为__format__()方法的参数使用。

2.2.2 集合和委托格式规范

当格式化一个包含集合的对象时,我们有两个难题:如何格式化整个对象和如何格式化集合中的对象。以Hand为例,其中包含了Cards类的集合。我们会更希望可以将Hand中一部分格式化的逻辑委托给Card实例完成。

下面是Hand中的format()方法。

def __format__( self, format_specification ):
   if format_specification == "":
     return str(self)
   return ", ".join( "{0:{fs}}".format(c, fs=format_specification)
     for c in self.cards )

Hand集合中的每个Card实例都会使用format_specification参数。对于每一个Card对象,都会使用内嵌格式规范的方法,用format_specification创建基于"{0:{fs}}"的格式。通过这样的方法,一个Hand对象,player_hand,可以以下面的方法格式化:

"Player: {hand:%r%s}".format(hand=player_hand)

这会将%r%s格式规范应用在Hand对象中的每个Card实例上。

相关文章
|
8天前
|
测试技术 API Python
【10月更文挑战第1天】python知识点100篇系列(13)-几种方法让你的电脑一直在工作
【10月更文挑战第1天】 本文介绍了如何通过Python自动操作鼠标或键盘使电脑保持活跃状态,避免自动息屏。提供了三种方法:1) 使用PyAutoGUI,通过安装pip工具并执行`pip install pyautogui`安装,利用`moveRel()`方法定时移动鼠标;2) 使用Pymouse,通过`pip install pyuserinput`安装,采用`move()`方法移动鼠标绝对位置;3) 使用PyKeyboard,同样需安装pyuserinput,模拟键盘操作。文中推荐使用PyAutoGUI,因其功能丰富且文档详尽。
|
1天前
|
Python
Python面向对象(2)
【10月更文挑战第14天】
Python面向对象(2)
|
2天前
|
设计模式 程序员 C语言
Python面向对象
【10月更文挑战第13天】
Python面向对象
|
5天前
|
机器学习/深度学习 数据采集 数据挖掘
11种经典时间序列预测方法:理论、Python实现与应用
本文将总结11种经典的时间序列预测方法,并提供它们在Python中的实现示例。
29 2
11种经典时间序列预测方法:理论、Python实现与应用
|
1天前
|
开发者 Python
Python中的魔法方法与运算符重载
在Python的奇妙世界里,魔法方法(Magic Methods)和运算符重载(Operator Overloading)是两个强大的特性,它们允许开发者以更自然、更直观的方式操作对象。本文将深入探讨这些概念,并通过实例展示如何利用它们来增强代码的可读性和表达力。
|
11天前
|
设计模式 安全 JavaScript
Python学习八:面向对象编程(下):异常、私有等
这篇文章详细介绍了Python面向对象编程中的私有属性、私有方法、异常处理及动态添加属性和方法等关键概念。
15 1
|
9天前
|
Linux Python
Python获得本机本地ip地址的方法
【10月更文挑战第8天】 socket模块包含了丰富的函数和方法,可以获取主机的ip地址,例如gethostbyname方法可以根据主机名获取ip地址,gethostbyname_ex方法可以获得本机所有ip地址列表,也可以使用netifaces模块获取网卡信息。
9 0
|
10天前
|
SQL 安全 数据库
Python防止SQL注入攻击的方法
Python防止SQL注入攻击的方法
20 0
|
11天前
|
Python
Python中tqdm模块的常用方法和示例
`tqdm` 是一个快速、可扩展的Python进度条库,适用于长循环中添加进度提示。通过封装迭代器 `tqdm(iterator)`,可以轻松实现进度显示。支持自定义描述、宽度及嵌套进度条,适用于多种迭代对象。在Jupyter notebook中,可自动调整显示效果。
19 0
|
11天前
|
Python
Python中threading模块的常用方法和示例
Python 的 `threading` 模块提供了多线程编程的能力,允许同时执行多个线程。主要类包括 `Thread`、`Lock` 和 `Condition`。`Thread` 类用于创建和管理线程,`Lock` 用于同步线程,防止资源竞争,`Condition` 用于线程间协调。本文介绍了这些类的常用方法及示例代码,帮助你更好地理解和使用多线程编程。
19 0