【问题标题】:Creating dynamic docstrings in Python descriptor在 Python 描述符中创建动态文档字符串
【发布时间】:2012-04-20 04:08:07
【问题描述】:

我正在尝试动态生成一些类定义(用于包装 C++ 扩展)。以下描述符工作正常,除非我尝试使用 help() 访问字段的文档字符串,它为描述符提供默认文档,而不是它自己的字段。但是,当我执行帮助(类名)时,它会检索传递给描述符的文档字符串:

class FieldDescriptor(object):
    def __init__(self, name, doc='No documentation available.'):
        self.name = name
        self.__doc__ = doc

    def __get__(self, obj, dtype=None):
        if obj is None and dtype is not None:
            print 'Doc is:', self.__doc__
            return self
        return obj.get_field(self.name)

    def __set__(self, obj, value):
        obj.set_field(self.name, value)

class TestClass(object):
    def __init__(self):
        self.fdict = {'a': None, 'b': None}

    def get_field(self, name):
        return self.fdict[name]

    def set_field(self, name, value):
        self.fdict[name] = value

fields = ['a', 'b']
def define_class(class_name, baseclass):
    class_obj = type(class_name, (baseclass,), {})
    for field in fields:
        setattr(class_obj, field, FieldDescriptor(field, doc='field %s in class %s' % (field, class_name)))
    globals()[class_name] = class_obj


if __name__ == '__main__':
    define_class('DerivedClass', TestClass)
    help(DerivedClass.a)
    help(DerivedClass)
    v = DerivedClass()
    help(v.a)

“python test.py”打印:

文档是:类 DerivedClass 中的字段 a 模块 __main__ 对象中 FieldDescriptor 的帮助: 类 FieldDescriptor(__builtin__.object) |此处定义的方法: | | __get__(self, obj, dtype=None) | | __init__(self, name, doc='没有可用的文档。') | | __set__(self, obj, value) | | -------------------------------------------------- -------------------- |此处定义的数据描述符: | | __dict__ |实例变量的字典(如果已定义) | | __weakref__ |对象的弱引用列表(如果已定义) 文档是:类 DerivedClass 中的字段 a 文档是:DerivedClass 类中的字段 b 模块 __main__ 中的 DerivedClass 类的帮助: 类派生类(TestClass) |方法解析顺序: |派生类 |测试类 | __builtin__.object | |此处定义的数据描述符: | |一种 |类 DerivedClass 中的字段 a | | b | DerivedClass 类中的字段 b | | -------------------------------------------------- -------------------- |从 TestClass 继承的方法: | | __在自身) | | get_field(自我,姓名) | | set_field(自我,姓名,价值) | | -------------------------------------------------- -------------------- |继承自 TestClass 的数据描述符: | | __dict__ |实例变量的字典(如果已定义) | | __weakref__ |对象的弱引用列表(如果已定义) 关于 NoneType 对象的帮助: 类无类型(对象) |此处定义的方法: | | __hash__(...) | x.__hash__() 哈希(x) | | __repr__(...) | x.__repr__() 代表(x)

知道如何获得descriptor.__doc__help(class.field) 吗? 有没有办法绕过这个并为 doc 提供一个 getter 函数,而不必将 doc 字符串存储在描述符中?

喜欢:

class FieldDescriptor(object):
    def __init__(self, name, doc='No documentation available.'):
        self.name = name
        self.__doc__ = doc

    def __get__(self, obj, dtype=None):
        if obj is None and dtype is not None:
            print 'Doc is:', self.__doc__
            return self
        return obj.get_field(self.name)

    def __set__(self, obj, value):
        obj.set_field(self.name, value)

    # This is what I'd like to have
    def __doc__(self, obj, dtype):
       return dtype.generate_docstring(self.name)

更新: 其实我是从__get__的这个定义开始的:

def __get__(self, obj, dtype=None):
    return obj.get_field(self.name)

问题在于,当我说:

help(DerivedClass.a)

Python 抛出了一个异常,表明我正在尝试调用 None.get_field。因此help() 使用obj=Nonedtype=DerivedClass 调用__get__ 方法。这就是为什么我决定在 obj=None 和 dtype!=None 时返回 FieldDescriptor 实例。 我的印象是help(xyz) 试图显示xyz.__doc__。按照这个逻辑,如果__get__ 返回descriptor_instance,那么descriptor_instance.__doc__ 应该由help() 打印,这是整个类[help(DerivedClass)] 的情况,但不是单个字段[help(DerivedClass.a)] .

【问题讨论】:

  • 我确定这一切都在那里,但您能否澄清哪些调用给您提供了错误的帮助输出?通过阅读代码来猜测您的期望太费力了。
  • 正如 jsbueno 指出的那样,是 help(DerivedClass.a) 显示描述符的文档而不是字段的文档(保存在描述符.__doc__ 中)。
  • @subhacom 你找到满意的答案了吗?
  • @Jérémie 据我记得,并在我对 jsbueno 答案的评论中提到,这似乎是由于 python 的内置 help 实现的工作方式。我最终为此编写了一个自定义帮助功能。这是一个相当复杂的项目的一部分,但代码在这里:github.com/BhallaLab/moose-core/blob/master/python/moose/…。 Python/C++接口代码在同一repo的pymoose目录下。

标签: python metaprogramming descriptor


【解决方案1】:

当你请求help(DerivedClass.a) 时,python 会计算括号内的表达式——这是描述符的__get__ 方法返回的对象——然后它们会搜索该对象的帮助(包括文档字符串)。

让这个工作(包括动态文档字符串生成)的一种方法是让您的__get__ 方法重新调整一个动态生成的对象,该对象具有所需的文档字符串。但是这个对象本身需要是原始对象的适当代理对象,并且会在您的代码上产生一些开销 - 以及许多特殊情况。

无论如何,让它像你想要的那样工作的唯一方法是修改 __get__ 本身返回的对象,以便它们的行为像你希望的那样。

我建议如果您在帮助中想要的只是一些信息,就像您正在做的那样,也许您希望从 __get__ 返回的对象属于定义 __repr__ 方法的类(而不仅仅是__doc__ 字符串)。

【讨论】:

  • __repr__ 方法不起作用。相反,它会打印实现__repr__ 的类的文档。使用属性而不是普通的描述符是可行的,但这会受到静态存储文档字符串的影响。使用 pdb 跟踪帮助函数发现 pydoc.help() 中的测试与 Python 中定义的描述符无关,尽管它们处理使用 C API 定义的属性和各种描述符。无论如何感谢您的意见,它鼓励我尝试其他方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 2013-11-20
相关资源
最近更新 更多