В IPython стандартный вывод отображает довольно напечатанное __repr__
представление объекта.В то время как в Python стандартный вывод print
s __repr__
представление объекта, короче, print(repr(obj))
.
Python :
Как вы будетеОбратите внимание, что стандартный вывод в Python аналогичен вызову функции print()
для repr(a)
.repr(a)
является объектным представлением a
и при вызове вызывает __repr__
.
>>> a = A()
>>> a
<__main__.A object at 0x000000D886391438>
>>> repr(a)
'<__main__.A object at 0x000000D886391438>'
>>> print(repr(a))
<__main__.A object at 0x000000D886391438>
IPython :
IPython, с другой стороны, имеетего собственная реализация отображения стандартного вывода и довольно печатает __repr__
для объекта перед отображением.Красивая печать объекта для stdout происходит в функции pretty()
, расположенной в классе RepresentationPrinter
в .. / IPython / lib / pretty.py :
def pretty(self, obj):
"""Pretty print the given object."""
obj_id = id(obj)
cycle = obj_id in self.stack
self.stack.append(obj_id)
self.begin_group()
try:
obj_class = _safe_getattr(obj, '__class__', None) or type(obj)
#<---code--->
Однакоперед вызовом pretty()
IPython вызывает метод __call__(self,obj)
в .. / IPython / core / formatters.py .Вы заметите это как самый верхний стек в ошибке исключения трассировки, и функция pretty()
выше вызывается в строке 702:
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)
В функции pretty()
над строкой _safe_getattr(obj, '__class__', None) or type(obj)
интересно.Определение для этой функции говорит, что это безопасная реализация getarr()
, что означает, что если при получении атрибута для этого объекта вызывается исключение, он вернет None
:
def _safe_getattr(obj, attr, default=None):
"""Safe version of getattr.
Same as getattr, but will return ``default`` on any Exception,
rather than raising.
"""
try:
return getattr(obj, attr, default)
except Exception:
return default
В pretty()
функция, значение _safe_getattr(obj, '__class__', None) or type(obj)
сохраняется в obj_class
.Позже в той же функции эта переменная передается в _get_mro()
.Это показано во втором стеке исключения трассировки в строке 382:
~/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
Задача _get_mro(obj_class)
- получить MRO (порядок разрешения методов) для obj_class
.В Python 3 все классы новый стиль и имеют атрибут __mro__
.Однако определения классов старого стиля были сохранены для обратной совместимости и не имеют этого атрибута.Ваш класс определен с синтаксисом старого стиля.Вы можете узнать больше о NewClass v / s OldClass здесь .В определении для _get_mro(obj_class)
ваш код попадает в блок try для синтаксиса старого стиля и выдает ошибки.Это самый последний и самый нижний стек в исключении трассировки:
~/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:
Итак, что происходит :
Давайте использовать все, что мы узнали, и понять, чтодействительно происходит за кулисами.Я изменил ваш код ниже, чтобы использовать вышеуказанные функции из модуля IPython.Попробуйте это сделать на консоли IPython / записной книжке Jupyter:
In [1]: from IPython.lib.pretty import _safe_getattr
...: from IPython.lib.pretty import pretty
...: from IPython.lib.pretty import _get_mro
...:
...: 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 = A()
...: a.test_attr = 'test_string'
In [2]: getattr_res = _safe_getattr(a, 'test_attr') or type(a)
In [6]: getattr_res
Out[6]: 'test_string!'
In [10]: getattr_res == getattr(a, 'test_attr')
Out[10]: True
Я определил атрибут test_attr
, в котором хранится строка 'test_string', поскольку вы упомянули, что все атрибуты str
.Переменная getattr_res
хранит значение для вызова _safe_getattr(a, 'test_attr')
, которое совпадает с вызовом getattr(a, 'test_attr')
, который в основном вызывает __getattribute__
в вашем коде:
In [13]: a.__getattribute__('test_attr')
Out[13]: 'test_string!'
Как вы заметите, getattr_res
имеет типстрока и строковые объекты не имеют атрибута __mro__
.У нас должен быть объект класса, чтобы получить MRO:
In [14]: type(getattr_res)
Out[14]: str
In [15]: _get_mro(getattr_res)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-15-d0ae02b5a6ac> in <module>()
----> 1 _get_mro(getattr_res)
C:\ProgramData\Anaconda3\lib\site-packages\IPython\lib\pretty.py in _get_mro(obj_class)
316 # Old-style class. Mix in object to make a fake new-style class.
317 try:
--> 318 obj_class = type(obj_class.__name__, (obj_class, object), {})
319 except TypeError:
320 # Old-style extension type that does not descend from object.
AttributeError: 'str' object has no attribute '__name__'
Это исключение выглядит знакомым, не так ли?Вызов функции _safe_getattr(obj, '__class__', None)
IPython вызывает __getattribute__
в вашем коде, который возвращает строковый объект, который не имеет атрибута __mro__
, и даже если _get_mro(obj_class)
пытается выполнить в блоке try
, мы получаем AttributeError
, потому чтомы знаем, что str
объекты не имеют атрибута '__name__'
:
In [16]: getattr_res.__name__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-16-0d8ba2c5af23> in <module>()
----> 1 getattr_res.__name__
AttributeError: 'str' object has no attribute '__name__'
Как мы можем это исправить :
В IPython возможнодобавьте наши собственные красивые правила печати для объектов в нашем классе.Вдохновленный документами для модуля lib.pretty , я изменил код и определил функцию _repr_pretty_(self, p, cycle)
, которая явно вызывается в __getattribute__
(после проверки типа) для отображения объекта в желаемомформат.Если атрибут является строкой, он просто возвращает строку снова:
In [20]: class A:
...: def __init__(self):
...: self.a = 1
...:
...: def __getattribute__(self, k):
...: attr = object.__getattribute__(self, k)
...: if type(attr) != types.MethodType:
...: return self._repr_pretty_(attr, cycle=False)
...: return attr
...:
...: def _repr_pretty_(self, p, cycle):
...: if cycle:
...: p.text('MyList(...)')
...: else:
...: if isinstance(p,str):
...: return p
...: return p.text(repr(self) + '!')
In [21]: a = A()
In [22]: a
Out[22]: <__main__.A object at 0x0000005E6C6C00B8>!
In [24]: a.test = 'test_string'
In [25]: a.test
Out[25]: 'test_string'
Обратите внимание, что cycle=False
при вызове _repr_pretty_()
в __getattribute__(self, k)
, поскольку attr
не является итеративным.
Как правило, рекомендуется добавить функцию __repr__
в ваш класс, поскольку она четко показывает представление объектов в вашем классе.Подробнее об этом можно прочитать здесь .
Заключение : стандартный вывод IPython реализует свой собственный довольно печатный __repr__
в отличие от интерпретатора Python, который использует встроенную функцию repr()
для stdout.Чтобы изменить поведение стандартного вывода на IPython, можно добавить функцию _repr_pretty_()
в их класс для отображения вывода по желанию.