Python repr() 函数(保姆级教程)

Python repr() 函数:让对象“自我表达”的秘密武器

在 Python 的众多内置函数中,repr() 可能不是最常被提及的,但它却是调试、日志记录和对象理解中的隐形英雄。如果你曾经在调试时看到过一堆看不懂的 <object at 0x...>,那很可能就是因为没有正确使用 repr()。今天我们就来彻底搞懂这个函数——它不只是“打印对象”,更是让程序自我表达的桥梁。

想象一下,你是一个程序员侦探,正在调查一个神秘的变量。你看到它打印出来是 "<__main__.Person object at 0x7f8c1a2b3c40>",这就像在看一张模糊的照片,根本无法判断它到底是谁。但如果你用 repr(),它会告诉你:“我是一个 Person 类的实例,名字叫 张三,年龄 25。”——这就是 repr() 的价值:提供对象的“可读性表示”


repr() 函数的基本用法与返回值

repr() 是 Python 内置函数,它的作用是返回一个对象的“官方”字符串表示形式。和 str() 不同,repr() 更强调准确性可重现性,适合用于调试和开发阶段。

number = 42
print(repr(number))   # 输出: 42
print(str(number))    # 输出: 42

text = "Hello, World!"
print(repr(text))     # 输出: 'Hello, World!'
print(str(text))      # 输出: Hello, World!

注意repr() 会给字符串加上引号,而 str() 不加。这是因为 repr() 的目标是“让代码能还原对象”,所以必须保留原始格式。

再看一个例子:

data = [1, 2, 3, "abc"]
print(repr(data))     # 输出: [1, 2, 3, 'abc']
print(str(data))      # 输出: [1, 2, 3, abc]

info = {"name": "李四", "age": 30}
print(repr(info))     # 输出: {'name': '李四', 'age': 30}
print(str(info))      # 输出: {'name': '李四', 'age': 30}

在这里你会发现,repr() 保留了字符串的引号,而 str() 则直接输出内容。这种差异在调试时非常关键——如果你看到 repr() 输出的 'abc',就知道这是一个字符串,而不是一个变量名。


repr() 与 str() 的核心区别

这是初学者最容易混淆的地方。我们用一张表格来清晰对比两者:

特性 repr() str()
目标 提供“可重现的”表示,用于调试 提供“用户友好”的表示,用于显示
字符串引号 保留引号(如 'abc' 不保留引号(如 abc
数字类型 保持原始格式(如 42 保持原始格式(如 42
对象表示 通常包含类名和内存地址(如 <Person object at 0x...> 通常更简洁(如 Person(name='张三')
使用场景 调试、日志、开发环境 用户界面、打印输出

形象比喻
str() 像是一位导游,只告诉你“这里有个湖”,简洁明了;
repr() 像是一位科学家,会说“这是位于北纬 30.123° 的人工湖,编号 H-2024-001,水深 2.5 米”——细节丰富,便于还原。


自定义类中的 repr() 方法

当我们在定义自己的类时,repr() 的作用更加突出。Python 允许我们通过重写 __repr__ 方法来控制对象的字符串表示。

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

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

s = Student("王五", 18, "高三")
print(repr(s))  # 输出: Student(name='王五', age=18, grade=高三)
print(s)        # 输出: Student(name='王五', age=18, grade=高三)

关键点:当使用 print()repr() 时,Python 会自动调用对象的 __repr__ 方法。如果没有定义,它会使用默认的 <类名 object at 0x...> 格式。

我们再来看一个更复杂的例子,模拟一个数据库记录:

class Record:
    def __init__(self, id, title, created_at):
        self.id = id
        self.title = title
        self.created_at = created_at

    def __repr__(self):
        return f"Record(id={self.id}, title='{self.title}', created_at='{self.created_at}')"

record = Record(101, "用户注册", "2024-05-20 10:30:00")
print(repr(record))  # 输出: Record(id=101, title='用户注册', created_at='2024-05-20 10:30:00')

这个输出非常有用,因为你可以直接复制这段字符串,粘贴到代码中,重新创建一个完全相同的对象——这就是 repr() 的“可重现”特性。


repr() 在调试与日志中的实际应用

在真实项目中,repr() 的价值体现在日志和异常处理中。

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

def process_data(data):
    logger.debug(f"接收到的数据: {repr(data)}")  # 使用 repr() 保留原始结构
    if not isinstance(data, list):
        raise ValueError(f"期望的是列表,但收到: {repr(data)}")

process_data([1, 2, 3])
process_data("not a list")  # 会触发异常,日志中显示: 接收到的数据: 'not a list'

在日志中使用 repr(),可以清晰地看到传入的参数类型和值,避免因为字符串缺失引号而误解。

再看一个常见场景:处理 JSON 数据时,repr() 可以帮你检查嵌套结构。

import json

raw_data = '{"name": "赵六", "scores": [85, 92, 78]}'
parsed = json.loads(raw_data)

print(repr(parsed))  # 输出: {'name': '赵六', 'scores': [85, 92, 78]}

即使嵌套结构复杂,repr() 也能保持清晰的层级关系,帮助你快速定位问题。


常见误区与最佳实践

误区一:认为 repr() 只用于字符串

很多人以为 repr() 只对字符串有用,其实它对所有对象都适用。即使是自定义类、函数、模块,repr() 都能提供有意义的表示。

def my_func():
    pass

print(repr(my_func))  # 输出: <function my_func at 0x...>
print(repr(len))      # 输出: <built-in function len>

误区二:忽略 repr()str() 的区别

print() 中,你通常用 str(),但在 repr() 中,它会调用 __repr__。如果只重写了 __str__,而没有重写 __repr__,那么 repr() 会退化为默认格式。

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

    def __str__(self):
        return f"人类: {self.name}"

p = Person("钱七")
print(str(p))   # 输出: 人类: 钱七
print(repr(p))  # 输出: <__main__.Person object at 0x...>

最佳实践:在自定义类中,建议同时重写 __str____repr____repr__ 应该尽可能详细,适合调试。


总结:为什么你应该掌握 Python repr() 函数

repr() 函数虽然低调,却是 Python 开发者工具箱中不可或缺的一环。它不仅能让你的调试过程更高效,还能在日志、异常处理、对象序列化等场景中发挥关键作用。

记住:

  • repr() 提供的是“可重现的”字符串表示
  • 它在调试中比 str() 更有用
  • 自定义类时,一定要考虑 __repr__ 的实现
  • 它能帮你“看清”对象的真相,而不是只看到表象

当你下次在控制台看到一堆 <object at 0x...> 时,别慌,用 repr() 一查,真相就浮出水面。Python repr() 函数,就是你与代码对话的“显微镜”。