我正在尝试在 python 类上实现
_repr_html_
(docs)。
该类是一个只读外观,用于使用属性表示法导航类似 JSON 的对象(基于 Fluent Python、Rahmalho (O'Reilly) 的示例 19-5)。它有一个自定义的
__getatrr__
方法来实现此行为:
from collections import abc
class FrozenJSON:
def __init__(self, mapping):
self._data = dict(mapping)
def __repr__(self):
return "FrozenJSON({})".format(repr(self._data))
def _repr_html_(self):
return (
"<ul>"
+ "\n".join(
f"<li><strong>{k}:</strong> {v}</li>"
for k, v in self._data.items()
)
+ "</ul>"
)
def __getattr__(self, name):
if hasattr(self._data, name):
return getattr(self._data, name)
else:
return FrozenJSON.build(self._data[name])
@classmethod
def build(cls, obj):
if isinstance(obj, abc.Mapping):
return cls(obj)
elif isinstance(obj, abc.MutableSequence):
return [cls.build(item) for item in obj]
else:
return obj
def __dir__(self):
return list(self._data.keys())
班级的行为如下:
>>> record = FrozenJSON({"name": "waldo", "age": 32, "occupation": "lost"})
>>> record.occupation
'lost'
但是
_repr_html_
在 IPython 环境中不会显示(我尝试过 vscode 和 jupyter lab)。
注释掉
__getattr__
方法会导致显示 HTML 表示形式,因此我相当有信心问题与此有关。
(
_repr_html_
其他对象在我的环境中工作正常(例如 pandas DataFrames)。)
以下内容没有帮助:
def __getattr__(self, name):
if hasattr(self._data, name):
return getattr(self._data, name)
elif name == "_repr_html_":
return self._repr_html_
else:
return FrozenJSON.build(self._data[name])
我不太了解 vscode / juptyer 实验室如何知道调用
_repr_html_
而不是 __repr__
,以及这个 __getattr__
如何打破这一点。
预先感谢您的帮助!
IPython 检查不存在的属性是否会为类引发
AttributeError
。如果发生这种情况,显然一切都好并且使用 _repr_html_
。如果没有为不存在的属性引发 AttributeError
,则使用 __repr__
。
(可以通过在
name
内打印__getattr__
来找出是什么属性;它被称为_ipython_canary_method_should_not_exist_
。)
我(还)不知道 IPython 到底是如何做到这一点的,更重要的是,为什么这样做。
在您的情况下,不存在的属性将引发
KeyError
,因为该行
return FrozenJSON.build(self._data[name])
因此,请确保当未找到
__getattr__
时,AttributeError
会引发实际的 name
,例如如下:
def __getattr__(self, name):
if hasattr(self._data, name):
return getattr(self._data, name)
else:
try:
return FrozenJSON.build(self._data[name])
except KeyError:
raise AttributeError('no such attribute')
现在 IPython 很高兴,你的 HTML 将显示在笔记本中。