python中__init__后面加特殊符号_可以格式化Python自定义对象的3个魔术方法

966189f9b24ea88657394d57f0207626.png

在Python中,下划线用于属性名时具有特殊含义。一种特殊形式是使用两对双下划线,一个在属性名之前,另一个在属性名之后,这被称为特殊方法或魔术方法。

例如,我们大多数人知道的第一个特殊方法可能是初始化方法__init__,它用于创建Python对象。下面的代码显示了一个示例:

class Student:

def __init__(self, name):

self.name = name

当你创建这个类的实例对象时,你可以通过在交互式控制台中输入对象变量来检查这个对象:

>>> student0 = Student("John Smith")

>>> student0

然而,这个对象的信息并不是很友好,它只是显示了它的类和内存地址。为了使它更有趣,我们应该考虑定制类的字符串格式化方法。具体来说,我们将在本文中讨论三种特殊的方法。

repr方法

__repr__方法与对象的表示相关,将对象转化为供解释器读取的形式。如上所示,Python对象在交互式控制台中输出其表示字符串。下面的代码显示了这个特性:

>>> class Student:

... def __init__(self, name):

... self.name = name

...

... def __repr__(self):

... return "__repr__ is called"

...

>>> student0 = Student("John Smith")

>>> student0

__repr__ is called

需要注意的一点是,我们应该为__repr__特殊方法返回一个字符串。如果返回一个非字符串,就会遇到TypeError,如下图所示。

fcde23eecca38bc87e002dabb354c31f.png

但是尚未解决的问题是,我们应该为对象返回什么字符串?根据经验来说,我们应该返回一个字符串,用户可以使用它重新构造对象(例如,创建一个与被检查对象具有相等值的对象)。

class Student:

def __init__(self, name):

self.name = name

def __repr__(self):

return f"{self.__class__.__name__}({self.name!r})"

使用更新后的代码,我们可以在交互式控制台中检查对象,如下所示。如你所见,表示字符串是一个有效的Python表达式,我们可以使用它创建一个Student对象:

>>> student0 = Student("John Smith")

>>> student0

Student('John Smith')

同样,我们可以使用内置的repr()方法来检索表示字符串。因为我们可能希望通过将字符串表示发送给内置的eval()函数来重新构造另一个对象:

>>> student1 = eval(repr(student0))

>>> student1

Student('John Smith')

顺便提一下,我们经常会使用f-string来插入对象的属性,我们使用!r来指定我们想要该字符串的原始表示,因为默认情况下,f-string会调用__str__方法来检索被插入变量的值。在这种情况下,省略!r将使字符串本身被使用,而不是引号内的字符串。下面是一个简单的例子——显然,后一个字符串不能用于创建Student对象:

>>> name = "John Smith"

>>> print(f"Student({name!r})")

Student('John Smith')

>>> print(f"Student({name})")

Student(John Smith)

另一件需要注意的事情是,有时提供一个允许重建对象的有意义的字符串是不实际的。在这种情况下,我们通常使用<>来封装实例的类和一些摘要信息。下面的代码展示了内置类的示例。

>>> from io import BytesIO

>>> BytesIO(b'Medium')

>>> with open("test.text", 'w') as file:

... print(repr(file))

...

str方法

另一个与对象格式化相关的有趣的特殊方法是__str__方法。下面的代码向您展示了如何在自定义类中重写它。

class Student:

def __init__(self, name):

self.name = name

def __str__(self):

return "__str__ is called"

使用修改后的类,让我们看看什么时候调用它。

>>> print(student0)

__str__ is called

>>> str(student0)

'__str__ is called'

>>> f"{student0}"

'__str__ is called'

如上所示,至少有三种情况会导致调用__str__特殊方法。具体来说,print()函数、str()函数(这是预期的,因为它只是一个语法糖)和f-string插值变量都将为对象调用底层的__str__方法。

虽然我们知道使用__str__方法的情况,但问题是实现这个方法的最佳实践是什么。

>>> class Student:

... def __init__(self, name):

... self.name = name

...

... def __str__(self):

... return f"{self.__class__.__name__}, {self.name}"

...

...

... student0 = Student("John Smith")

... print(student0)

...

Student, John Smith

虽然没有通用的方法来定义__str__方法返回的字符串。但是原则是我们应该返回一些关于实例对象的描述性信息。在大多数情况下,我们只返回实例对象的属性来表示对象就可以了。

除了这些点,应该注意的是,如果你的类没有定义__str__方法,Python将查找是否实现了__repr__。如果是,任何调用__str__的函数都会回退到调用__repr__。

format方法

另一个与对象字符串格式化相关的重要特殊方法是__format__方法。下面的代码显示了这个方法在自定义类中的使用方式:

class Student:

def __init__(self, name):

self.name = name

def __format__(self, format_spec):

return "__format__ is called"

正如我们之前所做的,让我们先看看这个方法被调用的常见情况:

>>> student0 = Student("John Smith")

>>> f"{student0}"

'__format__ is called'

>>> format(student0)

'__format__ is called'

>>> print(student0)

>>> str(student0)

'<__main__.student object at>'

f-string和内置的format()方法都可以调用__format__方法。让我们以f-string为例,向你展示__format__是如何工作的。

你可能注意到,与__repr__和__str__方法不同,__format__方法有一个额外的参数名为format_spec。此参数定义如何将对象格式化为字符串。让我们看看下面代码片段中的一些自定义规范:

>>> class Student:

... def __init__(self, name):

... self.name = name

...

... def __str__(self):

... return f"{self.__class__.__name__}, {self.name}"

...

... def __format__(self, format_spec):

... if format_spec == "i":

... return "".join(x[0] for x in self.name.split())

... elif format_spec == "C":

... return self.name.upper()

... print("___delegate to the built-in format method for generic formatting___")

... return format(str(self), format_spec)

...

...

... student0 = Student("John Smith")

... print("format_spec: i", f"{student0:i}")

... print("format_spec: C", f"{student0:C}")

... print("format_spec: generic", f"{student0:-^30}")

...

format_spec: i JS

format_spec: C JOHN SMITH

___delegate to the built-in format method for generic formatting___

format_spec: generic -----Student, John Smith------

我们定义了两个自定义规范。当说明为i时,它代表首字母,我们返回学生的首字母。另一个规范是C,它代表大写,我们返回学生名的大写形式。除了这两个规范之外,我们还将默认的内置格式委托给适用的字符串格式化。回想一下,当我们使用f-string时,我们在冒号后面指定格式要求。在我们的示例中,当我们将规范指定为i和C时,我们确实得到了所需的格式。

总结

在本文中,我们回顾了Python中有关格式化的三个基本的特殊方法。这里是一个简短的回顾。

__repr__方法是显示一个字符串表示形式,要求返回的是一个有效的Python表达式,可以用来创建类似的对象。当它不适用时,考虑使用“<>”来提供类信息和其他有意义的特性。

__str__方法提供对象的描述性信息。

__format__方法提供了除基本格式之外的自定义格式规范。如果你希望你的对象对于不同的用例有不同的字符串表示,这是很有帮助的。

作者:Yong Cui

deephub翻译组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值