python 中的可变 repr 基于调用深度
variable repr in python based on call depth
我有一个(坏?)习惯,在 Python 中显示 classes 就像 Matlab 中的结构一样,其中每个属性及其值都以漂亮干净的布局打印。这是通过在 class.
中实现 __repr__
方法来完成的
在字典或列表中处理对象时,这种显示方式可能会让人分心。在这种情况下,我想做一个更基本的显示。
这是设想的伪代码:
def __repr__(self):
if direct_call():
return do_complicated_printing(self)
else:
#something simple that isn't a ton of lines/characters
return type(self)
在此代码中 direct_call()
表示这不是作为另一个显示调用的一部分调用的。也许这可能需要在堆栈中寻找 repr
?我将如何实施直接呼叫检测?
所以我可能有这样的东西:
>>> data
<class my_class> with properties:
a: 1
cheese: 2
test: 'no testing'
但在列表中我想要这样的显示:
>>> data2 = [data, data, data, data]
>>> data2
[<class 'my_class'>,<class 'my_class',<class 'my_class'>,<class 'my_class'>]
我知道我可以通过调用一些执行此操作的函数来强制显示这种类型,但我希望 my_class
能够控制这种行为,而无需用户额外的询问为此。
换句话说,这不是解决方案:
>>> print_like_I_want(data2)
想做这件事很奇怪,通常一个函数或方法应该做同样的事情,无论谁调用它。但在这种情况下,__repr__
只是为了程序员的方便,所以方便似乎是一个足够好的理由让它按照你要求的方式工作。
然而,不幸的是,您想要的实际上是不可能的,因为无论出于何种原因,list.__repr__
方法在堆栈中不可见。我在 Python 3.5.2 和 Python 3.8.1:
中进行了测试
>>> class ReprRaises:
... def __repr__(self):
... raise Exception()
...
>>> r = ReprRaises()
>>> r
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
>>> [r]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
如您所见,无论 repr
中的对象是否在列表中,堆栈都是相同的。 (堆栈上的 __repr__
帧属于 ReprRaises
class,而不是 list
。)
我还使用 inspect.stack
:
进行了测试
>>> import inspect
>>> class ReprPrints:
... def __repr__(self):
... print(*inspect.stack(), sep='\n')
... return 'foo'
>>> r = ReprPrints()
>>> r
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
foo
>>> [r]
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
[foo]
同样,对象本身与列表中的对象之间的调用堆栈没有明显区别;所以您的__repr__
没有什么可检查的。
因此,您可以获得的最接近的是某种 print_like_I_want
函数。这个 可以 至少以一种让每个 class 定义自己的行为的方式编写:
def pp(obj):
try:
_pp = obj._pp
except AttributeError:
print(repr(obj))
else:
print(_pp())
我能想到的减少按键次数的唯一方法是重载一元运算符,例如通常无用的 unary plus:
>>> class OverloadUnaryPlus:
... def __repr__(self):
... return 'foo'
... def __pos__(self):
... print('bar')
...
>>> obj = OverloadUnaryPlus()
>>> obj
foo
>>> +obj
bar
__repr__
旨在提供一个简短的、通常是程序化的对象显示。它被用作所有内置容器显示元素的首选方法。因此,您应该覆盖 __repr__
以提供您的简短输出。
__str__
是用于完整显示对象的函数。当您 print
一个对象时,它通常会出现。您也可以通过调用 str
来触发它。你应该把你的长花式输出放在 __str__
,而不是 __repr__
.
您唯一需要做的修改是在 REPL 中显式调用 print(obj)
或 str(obj)
而不是 repr(obj)
或 obj
。正如 所示,堆栈检查不会对您有多大帮助,而且在我看来,即使它有帮助也不是一个干净的解决方案。
我有一个(坏?)习惯,在 Python 中显示 classes 就像 Matlab 中的结构一样,其中每个属性及其值都以漂亮干净的布局打印。这是通过在 class.
中实现__repr__
方法来完成的
在字典或列表中处理对象时,这种显示方式可能会让人分心。在这种情况下,我想做一个更基本的显示。
这是设想的伪代码:
def __repr__(self):
if direct_call():
return do_complicated_printing(self)
else:
#something simple that isn't a ton of lines/characters
return type(self)
在此代码中 direct_call()
表示这不是作为另一个显示调用的一部分调用的。也许这可能需要在堆栈中寻找 repr
?我将如何实施直接呼叫检测?
所以我可能有这样的东西:
>>> data
<class my_class> with properties:
a: 1
cheese: 2
test: 'no testing'
但在列表中我想要这样的显示:
>>> data2 = [data, data, data, data]
>>> data2
[<class 'my_class'>,<class 'my_class',<class 'my_class'>,<class 'my_class'>]
我知道我可以通过调用一些执行此操作的函数来强制显示这种类型,但我希望 my_class
能够控制这种行为,而无需用户额外的询问为此。
换句话说,这不是解决方案:
>>> print_like_I_want(data2)
想做这件事很奇怪,通常一个函数或方法应该做同样的事情,无论谁调用它。但在这种情况下,__repr__
只是为了程序员的方便,所以方便似乎是一个足够好的理由让它按照你要求的方式工作。
然而,不幸的是,您想要的实际上是不可能的,因为无论出于何种原因,list.__repr__
方法在堆栈中不可见。我在 Python 3.5.2 和 Python 3.8.1:
>>> class ReprRaises:
... def __repr__(self):
... raise Exception()
...
>>> r = ReprRaises()
>>> r
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
>>> [r]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __repr__
Exception
如您所见,无论 repr
中的对象是否在列表中,堆栈都是相同的。 (堆栈上的 __repr__
帧属于 ReprRaises
class,而不是 list
。)
我还使用 inspect.stack
:
>>> import inspect
>>> class ReprPrints:
... def __repr__(self):
... print(*inspect.stack(), sep='\n')
... return 'foo'
>>> r = ReprPrints()
>>> r
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
foo
>>> [r]
FrameInfo(frame=<frame object at 0x7fcbe4a38588>, filename='<stdin>', lineno=3, function='__repr__', code_context=None, index=None)
FrameInfo(frame=<frame object at 0x7fcbe44fb388>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)
[foo]
同样,对象本身与列表中的对象之间的调用堆栈没有明显区别;所以您的__repr__
没有什么可检查的。
因此,您可以获得的最接近的是某种 print_like_I_want
函数。这个 可以 至少以一种让每个 class 定义自己的行为的方式编写:
def pp(obj):
try:
_pp = obj._pp
except AttributeError:
print(repr(obj))
else:
print(_pp())
我能想到的减少按键次数的唯一方法是重载一元运算符,例如通常无用的 unary plus:
>>> class OverloadUnaryPlus:
... def __repr__(self):
... return 'foo'
... def __pos__(self):
... print('bar')
...
>>> obj = OverloadUnaryPlus()
>>> obj
foo
>>> +obj
bar
__repr__
旨在提供一个简短的、通常是程序化的对象显示。它被用作所有内置容器显示元素的首选方法。因此,您应该覆盖 __repr__
以提供您的简短输出。
__str__
是用于完整显示对象的函数。当您 print
一个对象时,它通常会出现。您也可以通过调用 str
来触发它。你应该把你的长花式输出放在 __str__
,而不是 __repr__
.
您唯一需要做的修改是在 REPL 中显式调用 print(obj)
或 str(obj)
而不是 repr(obj)
或 obj
。正如