【问题标题】:Python decorated class documentationPython 装饰类文档
【发布时间】:2023-03-03 04:53:23
【问题描述】:

我在 python 中使用了单例的装饰器,实现如下所示。

我想让装饰类的 pydoc 与非装饰类的 pydoc 完全相同,但我不知道如何:

  • 没有getSingletonInstance.__doc__ = cls.__doc__这一行,装饰类的pydoc给出singleton函数的pydoc。

  • 使用 getSingletonInstance.__doc__ = cls.__doc__ 行,装饰类的 pydoc 只给出“顶级”文档字符串。

我该如何继续?

谢谢。

def singleton(cls):
    """A singleton decorator

    Warnings
    --------
    Singleton gdecorated calsses cannot be inhehited

    Example
    -------
    >>> from decorators import singleton
    >>> @singleton
    ... class SingletonDemo():
    ...     pass
    >>> d1 = SingletonDemo()
    >>> d1.a = 0xCAFE
    >>> d2 = SingletonDemo()
    >>> id(d1) == id(d2)
    True
    >>> d1 == d2
    True
    >>> d2.a == 0xCAFE
    True

    References
    ----------
    See case 2 of https://www.python.org/dev/peps/pep-0318/#examples
    """
    _instances = {}

    def getSingletonInstance():
        if cls not in _instances:
            _instances[cls] = cls()
        return _instances[cls]

    getSingletonInstance.__doc__ = cls.__doc__

    return getSingletonInstance

【问题讨论】:

  • 不不不不不不不。永远不要编写用函数替换类的装饰器。 type(SingletonDemo()) is SingletonDemo? Falseisinstance(SingletonDemo(), SingletonDemo)? TypeErrorclass SingletonSubclass(SingletonDemo):?还有TypeError。当你装饰一个类时,返回一个类。理想情况下,同一个班级。
  • 感谢 Aran-Fey,但是……好吧,我已经看到了很多关于单身人士的想法,但没有一个是完美的。蟒蛇宣言说:“尽管实用性胜过纯洁性。”。但是,如果您有更好的解决方案,我会很乐意学习 ;-) 我只是不想在每次需要实例时都调用 getInstance 类方法或类似的方法,但我想总是能够透明地使用MyClass()
  • 为什么不直接将实例设为模块级全局,并防止创建更多实例?您可能熟悉核心 Python 单例None,但您从未通过调用构造函数访问过它。你只需写None
  • 确实是@user2357112,但这意味着每次导入模块时都会创建实例。如果不需要,我不想创建实例。

标签: python python-3.x decorator docstring


【解决方案1】:

尝试使用functools.wraps

import functools

def wrapper(cls):
    _instances = {}
    class inner(cls):
        def __new__(subcls):
            if subcls not in _instances:
                _instances[subcls] = object.__new__(subcls)
            return _instances[subcls]
    inner.__doc__=cls.__doc__
    return inner

@wrapper
class A(object):
    """Example Docstring"""
    def method(self):
        """Method Docstring"

A.__doc__
# "Example Docstring"
A.method.__doc__
# "Method Docstring"

【讨论】:

  • 感谢@mousetail,但它与getSingletonInstance.__doc__ = cls.__doc__ 相同:只有“顶级”文档字符串,而不是完整的类文档
  • @MichaelHooreman 我已经更新了我的答案。它现在应该可以工作了
  • 太棒了!它只会错过构造函数调用。这是一种意大利面条代码,但我不得不在_instances[cls] = object.__new__(cls) 之后使用_instances[cls].__init__() 调用它。不幸的是,isinstance(A(), A) 仍然返回 False
  • @MichaelHooreman 我试过了,isinstance 适合我。
  • 确实,我打电话给__new__(cls),而不是__new__(subclass)。对不起。我在下面对“另一种”解决方案进行了总结。
【解决方案2】:

回答总结所有讨论,并举例说明。 非常感谢大家。

这个解决方案:

  • 保留文档字符串
  • 保留类型
  • 支持静态和类方法

限制:

  • 单例不能被继承,这对于上下文来说是有意义的

解决方案: def 单例(cls):

    """A singleton decorator

    Warnings
    --------
    Singleton decorated classes cannot be inhehited

    Example
    -------
    >>> import abc
    >>> 
    >>> @singleton
    ... class A():
    ...     "Ad-hoc documentation of class A"
    ...     def __init__(self):
    ...         "Ad-hoc documentation of class A constructor"
    ...         print("constructor called")
    ...         self.x = None
    ...     @classmethod
    ...     def cm(cls):
    ...         "a class method"
    ...         print("class method called")
    ...     def im(self):
    ...         "an instance method"
    ...         print("instance method called")
    ...     @staticmethod
    ...     def sm():
    ...         "a static method"
    ...         print("static method called")
    ... 
    >>> @singleton
    ... class P(abc.ABCMeta):
    ...     @abc.abstractmethod
    ...     def __init__(self):
    ...         pass
    ... 
    >>> class C(P):
    ...     def __init__(self):
    ...         print("C1 constructor called")
    ... 
    >>> a1 = A()
    constructor called
    >>> a1.x = 0xCAFE
    >>> a1.x
    51966
    >>> a2 = A()
    >>> a2.x
    51966
    >>> a1.x == a2.x
    True
    >>> a1 == a2
    True
    >>> id(a1) == id(a2)
    True
    >>> type(a1) == type(a2)
    True
    >>> isinstance(a1, A)
    True
    >>> ta1 = type(a1)
    >>> issubclass(ta1, A)
    True
    >>> A.cm()
    class method called
    >>> a1.cm()
    class method called
    >>> A.sm()
    static method called
    >>> a1.sm()
    static method called
    >>> a1.im()
    instance method called
    >>> try:
    ...     C()
    ... except Exception as e:
    ...     type(e)
    ... 
    <class 'TypeError'>

    """
    _instances = {}
    _constructorCalled = []
    class inner(cls):
        def __new__(subcls):
            if subcls not in _instances:
                _instances[subcls] = cls.__new__(subcls)
            return _instances[subcls]
        def __init__(self):
            if type(self) not in _constructorCalled:
                cls.__init__(self)
                _constructorCalled.append(type(self))
        __init__.__doc__ = cls.__init__.__doc__
        __new__.__doc__ = cls.__new__.__doc__
        if __new__.__doc__ == (
            "Create and return a new object.  "
            "See help(type) for accurate signature."
        ):
            __new__.__doc__ = "Returns a singleton instance"
    inner.__doc__ = cls.__doc__
    return inner

【讨论】:

    猜你喜欢
    • 2017-04-16
    • 2010-12-19
    • 2011-04-28
    • 2021-07-16
    • 1970-01-01
    • 2012-04-11
    • 2014-02-16
    • 2015-08-21
    • 1970-01-01
    相关资源
    最近更新 更多