Python中reprlib模块中的recursive_repr()函数的原理和实现方式
reprlib模块是Python标准库中的一个模块,用于提供对Python对象的更友好的打印输出。
reprlib.repr()函数是reprlib模块中的一个重要函数,它用于生成对象的紧凑显示形式,可用于输出或日志记录。然而,当对象的表示形式非常复杂或递归嵌套时,repr()函数输出的结果会变得非常长,并且很难阅读。
为了解决这个问题,reprlib模块提供了recursive_repr()装饰器函数。该函数可以应用于类的__repr__()方法,以便在表示对象时避免深度递归。
下面是一个使用recursive_repr()函数的示例:
import reprlib
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@reprlib.recursive_repr()
def __repr__(self):
return 'Point({}, {})'.format(self.x, self.y)
这个示例定义了一个简单的Point类,包含x和y两个坐标属性。在类的__repr__()方法上应用了reprlib.recursive_repr()装饰器。
当我们创建一个Point对象并打印它时,__repr__()方法会被调用,并且输出的结果会被reprlib模块处理:
p = Point(1, 2) print(p)
输出:
Point(1, 2)
在这个示例中,Point类的__repr__()方法使用了reprlib.recursive_repr()装饰器。这意味着当对象为自引用或互相嵌套时,输出结果不会无限递归下去。
reprlib.recursive_repr()装饰器的实现方式主要是利用了Python的上下文管理器。具体来说,它通过将递归状态保存到线程本地存储中,来避免递归过深导致的崩溃。
def recursive_repr(fillvalue='...'):
'Decorator to make a repr function return fillvalue for a recursive call'
def decorating_function(user_function):
repr_running = set()
def wrapper(self):
key = id(self), get_thread_ident()
if key in repr_running:
return fillvalue
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
# Looks like a regular repr
wrapper.__module__ = getattr(user_function, '__module__')
wrapper.__doc__ = getattr(user_function, '__doc__')
wrapper.__name__ = getattr(user_function, '__name__')
wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
return wrapper
return decorating_function
上面是reprlib模块中recursive_repr()装饰器的简化版本实现。它接受一个fillvalue参数,默认为'...',用于指定当递归发生时返回的填充值。装饰器使用一个repr_running集合来保存正在执行的__repr__()方法的对象的id和线程id。如果发现正在进行的递归调用,则直接返回填充值。
总结起来,recursive_repr()函数的原理是通过保存正在执行的__repr__()方法的对象和线程id,来判断是否发生递归调用,并返回填充值以避免无限递归。这样可以大大简化打印输出的结果,并提高可读性。
