Оформленная Python документация класса - PullRequest
0 голосов
/ 06 ноября 2019

Я использую декоратор для синглетонов в python, реализованный, как показано ниже.

Я бы хотел, чтобы pydoc для декорированного класса был точно таким же, как pydoc для не декорированного класса,но я не понимаю, как:

  • Без строки getSingletonInstance.__doc__ = cls.__doc__ pydoc декорированного класса дает pydoc функции singleton.

  • В строке getSingletonInstance.__doc__ = cls.__doc__ пидок декорированного класса дает только строку документа "верхнего уровня".

Как продолжить?

Спасибо.

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

Ответы [ 2 ]

0 голосов
/ 06 ноября 2019

Ответ, обобщив все обсуждения, с примером. Большое спасибо всем.

Это решение:

  • Сохраняет строку документации
  • Сохраняет тип
  • Поддерживает статические методы и методы класса

Ограничения:

  • Синглетоны не могут быть унаследованы, что имеет смысл в контексте

Решение: def singleton (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
0 голосов
/ 06 ноября 2019

Попробуйте использовать 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"
...