有两个问题。首先,描述符 API 也适用于类。因此,当 Ipython 尝试从类中获取描述符时,实例__get__
逻辑被调用,它恰好失败并出现AttributeError
,因此描述符被忽略。在您的示例中,如果您尝试从中获取属性Interface
,它会引发一个错误,因为它试图在一个实例上运行(这是None
在这种情况下):
In [25]: Interface.r
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-25-dd7a6e721f7e> in <module>
----> 1 Interface.r
<ipython-input-24-7d19c1ba4fe6> in __get__(self, obj, objtype)
5
6 def __get__(self, obj, objtype):
----> 7 return obj.read(self.address)
8
9 def __set__(self, obj, val):
AttributeError: 'NoneType' object has no attribute 'read'
第二个是,如果描述符是一个实例,Ipython 仅使用描述符来提供帮助property
(硬编码)。这样做的逻辑是here https://github.com/ipython/ipython/blob/6ddb518ca6ad0b4addfe9ae6e2ac88f4bc12e056/IPython/core/interactiveshell.py#L1615-L1648.
要解决第一个问题,如果为 obj 传递了 None,则需要返回描述符本身:
def __get__(self, obj, objtype=None):
if obj is None:
return self
...
要解决第二个问题,您要么需要向 ipython 提交补丁,要么从属性提交子类(更容易,尽管有点 hacky)。将这些放在一起:
class Register(property):
def __init__(self, address, docstring="instance docstring"):
self.address = address
self.__doc__ = docstring
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj.read(self.address)
def __set__(self, obj, val):
return obj.write(self.address, val)
class Interface(object):
r = Register(0x00, docstring="the first register")
i = Interface()
然后在 ipython 中,你会得到:
In [21]: i.r?
Type: Register
String form: <__main__.Register object at 0x1051203a0>
Docstring: the first register