Python中的__repr__() & __str__()

__repr__()

__repr__() 是 Python 中的一个特殊方法(也称为 “dunder” 方法,由 “double underscore” 缩写而来)。当调用一个对象的 repr() 函数或者在控制台中打印一个对象时,这个方法就会被调用。它的目标是返回一个字符串,这个字符串应该是一个合法的 Python 表达式,当通过 eval() 执行时,可以复现这个对象(尽管在实践中,很多时候我们不需要或不可能实现这种复现性)。

__repr__() 的主要目的是为调试和开发提供明确性。这个返回的字符串应该尽量详细,对于开发人员来说应该是明确的。

例如,对于之前的 Person 类:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"

如果我们创建一个 Person 对象:

p = Person("John", 30)

然后在控制台或代码中尝试打印这个对象:

print(p)

输出将会是:

Person(name=John, age=30)

没有 __repr__(),我们可能只会得到一个不太有用的字符串,比如 <__main__.Person object at 0x7f065209d0>,这个字符串只是告诉我们对象的内存地址,而不是它的内容。

还有另一个与此相关的方法,__str__(),它被 print()str() 调用。如果没有定义 __str__(), 则会默认回退到 __repr__(),但 __str__() 更侧重于为用户提供友好的或简洁的字符串表示。

__str__()

__str__() 是 Python 中的一个魔法方法或特殊方法,用于定义一个对象的"informal"或"nicely printable"的字符串表示。当使用 print() 函数或 str() 函数来获取一个对象的字符串表示时,__str__() 方法会被调用。

这个方法的目的是为对象提供一个用户友好的描述,而不像 __repr__() 那样为开发者提供一个严格的描述。换句话说,__str__() 的输出更倾向于为人类读者而设计,而不是为机器或开发者。

下面用一个简单的例子来解释 __str__() 的作用:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return f"'{self.title}' by {self.author}"

# 使用上述的Book类
book = Book("1984", "George Orwell")

print(book)  # 输出: '1984' by George Orwell

在上述示例中,__str__()Book 对象提供了一个简洁的描述,这样当我们打印 book 对象时,输出的字符串是用户友好的。如果没有实现 __str__(), Python 通常会回退到使用 __repr__() (如果已经定义)或者输出一个不太有用的默认字符串,例如 <__main__.Book object at 0x7f065209d0>

示例

让我们继续使用上述的 Person 类作为例子。在下面的示例中,我们将同时实现 __repr__()__str__() 方法:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        # 为开发者提供明确性,通常更详细,尽量确保能通过eval()复现对象(尽管并不总是这样)
        return f"Person(name='{self.name}', age={self.age})"

    def __str__(self):
        # 为最终用户提供易读性,更友好、简洁
        return f"{self.name}, {self.age} years old"

现在,当我们创建一个 Person 对象并尝试打印它:

p = Person("John", 30)

print(repr(p))  # 使用 __repr__
# 输出: Person(name='John', age=30)

print(p)  # 默认使用 __str__
# 输出: John, 30 years old

print(str(p))  # 显式地使用 __str__
# 输出: John, 30 years old

可以看到,当尝试直接打印对象或使用 str() 时,__str__() 被调用,而当使用 repr() 时,__repr__() 被调用。如果类中没有定义 __str__(), 则直接打印或使用 str() 的调用会回退到 __repr__()


当我们谈论 __repr__() 为对象提供一个明确且尽量能够通过 eval() 复现的表示时,意味着该方法返回的字符串,当作为 Python 表达式执行时,应该尽可能地创建一个与原始对象等价或相似的对象。

下面用一个例子来解释这个概念:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

# 使用上述的Point类
p = Point(3, 4)

# 打印对象的repr表示
print(repr(p))  # 输出: Point(3, 4)

# 使用eval函数来执行repr返回的字符串,创建一个新的对象
new_p = eval(repr(p))

# 验证新对象的属性
print(new_p.x)  # 输出: 3
print(new_p.y)  # 输出: 4

在上述例子中,Point 类的 __repr__() 方法返回一个字符串,这个字符串看起来像是一个调用 Point 构造函数的表达式。当我们用 eval() 函数执行这个字符串时,它实际上创建了一个新的 Point 对象,其属性与原始对象相同。

这种方法使得 __repr__() 产生的输出对于调试和记录非常有价值,因为它提供了创建相同对象的明确“配方”。然而,请注意,并不总是可能或适合确保 __repr__() 的输出可以完全通过 eval() 复现,尤其是当对象的初始化涉及非常复杂的参数或状态时。在这些情况下,__repr__() 的目标仍然是提供尽可能明确和有用的描述。

你可能感兴趣的:(Python,python)