我有一个自定义的__getattribute__
,如果成员不是方法(因此是属性),它应该修改返回值。假设所有属性(self.a、self.b等)都是str
。你知道吗
class A:
def __init__(self):
self.a = 1
def __getattribute__(self, k):
attr = object.__getattribute__(self, k)
if type(attr) != types.MethodType:
return '{}!'.format(attr)
return attr
在获取类A
实例的表示时,我在IPython中遇到了一个错误,但我不明白为什么。
例如:
In [26]: a = A()
In [27]: a
Out[27]: ---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
~/miniconda3/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
700 type_pprinters=self.type_printers,
701 deferred_pprinters=self.deferred_printers)
--> 702 printer.pretty(obj)
703 printer.flush()
704 return stream.getvalue()
~/miniconda3/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
380 # 1) a registered printer
381 # 2) a _repr_pretty_ method
--> 382 for cls in _get_mro(obj_class):
383 if cls in self.type_pprinters:
384 # printer registered in self.type_pprinters
~/miniconda3/lib/python3.7/site-packages/IPython/lib/pretty.py in _get_mro(obj_class)
318 # Old-style class. Mix in object to make a fake new-style class.
319 try:
--> 320 obj_class = type(obj_class.__name__, (obj_class, object), {})
321 except TypeError:
322 # Old-style extension type that does not descend from object.
AttributeError: 'str' object has no attribute '__name__'
但是print(a)
工作正常
In [33]: print(a)
<__main__.A object at 0x10c566390>
注意:在普通Python REPL中,它似乎工作正常。
>>> a = A()
>>> a
<__main__.A object at 0x1032b9320>
在IPython中,标准输出显示对象的一个漂亮的
__repr__
表示。在Python的输出中。你知道吗Python:
正如您将在下面注意到的,Python中的标准输出与在
repr(a)
上调用print()
函数相同。repr(a)
是a
的对象表示,在调用时调用__repr__
。你知道吗伊皮顿:
另一方面,IPython有自己的实现来显示标准输出,并在显示之前漂亮地打印对象的
__repr__
。stdout对象的漂亮打印发生在pretty()
函数中,该函数位于./IPython/lib中的RepresentationPrinter
类中/很漂亮:但是,在调用
pretty()
之前,IPython调用./IPython/core中的__call__(self,obj)
方法/格式化程序.py。您将注意到这是回溯异常错误中最顶层的堆栈,并且在第702行调用了上面的pretty()
函数:在
pretty()
函数中,_safe_getattr(obj, '__class__', None) or type(obj)
行是有趣的。如果这个函数的定义是安全的,那么它将返回一个异常:在
pretty()
函数中,_safe_getattr(obj, '__class__', None) or type(obj)
的值存储在obj_class
。稍后,在同一个函数中,这个变量被传递给_get_mro()
。这在第382行的回溯异常的第二个堆栈中显示:_get_mro(obj_class)
的任务是获取obj_class
的MRO(方法解析顺序)。在python3中,所有类都是新样式,并具有__mro__
属性。但是,为了向后兼容,保留了旧样式类定义,并且没有此属性。您的类是用旧式语法定义的。您可以阅读有关NewClass v/s OldClasshere的更多信息。在_get_mro(obj_class)
的定义中,您的代码属于旧样式语法和错误输出的try块。这是回溯异常中最新和最底层的堆栈:发生了什么事
让我们利用所学的一切,了解幕后真正发生的事情。我修改了上面的ipythi模块中的函数来使用下面的代码。您应该在IPython控制台/Jupyter笔记本上尝试以下操作:
我已经定义了一个属性
test_attr
,它存储一个字符串“test\u string”,正如您所提到的,所有属性都是str
。getattr_res
变量存储调用_safe_getattr(a, 'test_attr')
的值,该值与调用getattr(a, 'test_attr')
的值相同,后者基本上在代码中调用__getattribute__
:正如您将看到的,
getattr_res
是string类型,而string对象没有__mro__
属性。我们应该有一个类对象来获取MRO:这个例外看起来很熟悉,不是吗?对IPython的
_safe_getattr(obj, '__class__', None)
函数的调用调用了代码中的__getattribute__
,它返回一个没有__mro__
属性的字符串对象,即使_get_mro(obj_class)
尝试在try
块中执行,我们也会得到一个AttributeError
,因为我们知道str
对象没有'__name__'
属性:我们如何解决这个问题:
在IPython中,可以为类中的对象添加我们自己的漂亮打印规则。受docs for module lib.pretty的启发,我修改了代码并定义了一个
_repr_pretty_(self, p, cycle)
函数,该函数在__getattribute__
(类型检查之后)中显式调用,以所需的格式显示对象。如果属性是一个字符串,它只需再次返回字符串:请注意,调用
__getattribute__(self, k)
中的_repr_pretty_()
时cycle=False
,因为attr
不是iterable。你知道吗一般来说,建议在类中添加
__repr__
函数,因为它清楚地显示了对象i的表示形式在你们班上。您可以阅读更多关于它的信息here。你知道吗结论:IPython标准输出实现了自己漂亮的
__repr__
,而Python解释器使用stdout的内置repr()
函数。为了改变IPython上stdout的行为,可以向它们的类中添加_repr_pretty_()
函数来显示所需的输出。你知道吗相关问题 更多 >
编程相关推荐