描述符的Python docstring

Leo*_*aus 7 python docstring descriptor ipython python-2.7

我使用描述符来定义接口类的寄存器:

class Register(object):
    def __init__(self, address, docstring="instance docstring"):
        self.address = address    
        self.__doc__ = docstring

    def __get__(self, obj, objtype):
        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") 
Run Code Online (Sandbox Code Playgroud)

我喜欢ipython的用户能够执行以下操作之一:

i = Interface()
i.r? #should show the docstring "the first register"
Run Code Online (Sandbox Code Playgroud)

要么

i = Interface()
i.r(  #should show the docstring "the first register" when parentheses are opened
Run Code Online (Sandbox Code Playgroud)

但是,docstring始终是obj.read返回的int对象中的文件串,而不是指定的docstring.有没有办法在这种情况下显示正确的文档字符串?

如果我不使用描述符但是手动定义它们,则在括号打开时它会起作用:

class Interface(object):
    @property
    def r(self):
        """this docstring will be shown alternatively"""
        return self.read(0x0)

    @r.setter
    def r(self,v):
        """this is the docstring that is shown"""
        self.write(0x0,v)

i = Interface()
i.r( #the right docstring pops up here once i open the bracket
Run Code Online (Sandbox Code Playgroud)

如果setter没有定义文档字符串,则在打开括号时会显示其中一个getter.

我可以通过使用描述符以某种方式获得相同的行为而没有不合理的开销吗?

我的问题与此问题有些类似,但是没有给出令人满意的答案: 在Python描述符中创建动态文档字符串

Rec*_*nic 3

有两个问题。首先,描述符 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'
Run Code Online (Sandbox Code Playgroud)

第二个是,如果描述符是property(硬编码)的实例,Ipython 仅使用描述符来提供帮助。这个的逻辑就在这里

要解决第一个问题,如果为 obj 传递了 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'
Run Code Online (Sandbox Code Playgroud)

要解决第二个问题,您要么需要向 ipython 提交补丁,要么从属性提交子类(更容易,尽管有点 hacky)。将这些放在一起:

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        ...
Run Code Online (Sandbox Code Playgroud)

然后在 ipython 中,你会得到:

In [21]: i.r?
Type:        Register
String form: <__main__.Register object at 0x1051203a0>
Docstring:   the first register
Run Code Online (Sandbox Code Playgroud)