14

Python class inheritance に Inherit docstringsに関する質問がありますが、そこでの回答はメソッドの docstring を扱っています。

私の質問は、親クラスの docstring を__doc__属性として継承する方法です。ユースケースは、Django レスト フレームワークが、ビュー クラスのドキュメント文字列に基づいて API の HTML バージョンで適切なドキュメントを生成することです。しかし、docstring を持たないクラスで基本クラス (docstring を持つ) を継承する場合、API は docstring を表示しません。

スフィンクスやその他のツールが正しいことを行い、docstring の継承を処理してくれる可能性は非常に高いですが、django レスト フレームワークは (空の).__doc__属性を調べます。

class ParentWithDocstring(object):
    """Parent docstring"""
    pass


class SubClassWithoutDoctring(ParentWithDocstring):
    pass


parent = ParentWithDocstring()
print parent.__doc__  # Prints "Parent docstring".
subclass = SubClassWithoutDoctring()
print subclass.__doc__  # Prints "None"

のようなことを試しましsuper(SubClassWithoutDocstring, self).__doc__たが、それもNone.

4

4 に答える 4

14

(少なくともCPythonでは)クラスに新しいdocstringを割り当てることはできない__doc__ため、メタクラスを使用する必要があります。

import inspect

def inheritdocstring(name, bases, attrs):
    if not '__doc__' in attrs:
        # create a temporary 'parent' to (greatly) simplify the MRO search
        temp = type('temporaryclass', bases, {})
        for cls in inspect.getmro(temp):
            if cls.__doc__ is not None:
                attrs['__doc__'] = cls.__doc__
                break

    return type(name, bases, attrs)

はい、余分なフープを1つか2つジャンプしますが、上記のメタクラスは__doc__、継承グラフを作成する際に、どのように複雑であっても正しいものを見つけます。

使用法:

>>> class ParentWithDocstring(object):
...     """Parent docstring"""
... 
>>> class SubClassWithoutDocstring(ParentWithDocstring):
...     __metaclass__ = inheritdocstring
... 
>>> SubClassWithoutDocstring.__doc__
'Parent docstring'

別の方法は、インスタンス変数としてで設定__doc__することです。__init__

def __init__(self):
    try:
        self.__doc__ = next(cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None)
    except StopIteration:
        pass

次に、少なくともインスタンスにdocstringがあります。

>>> class SubClassWithoutDocstring(ParentWithDocstring):
...     def __init__(self):
...         try:
...             self.__doc__ = next(cls.__doc__ for cls in inspect.getmro(type(self)) if cls.__doc__ is not None)
...         except StopIteration:
...             pass
... 
>>> SubClassWithoutDocstring().__doc__
'Parent docstring'

Python 3.3(問題12773を修正)では、最終的にカスタムクラスの属性を設定できる__doc__ようになったため、代わりにクラスデコレータを使用できます。

import inspect

def inheritdocstring(cls):
    for base in inspect.getmro(cls):
        if base.__doc__ is not None:
            cls.__doc__ = base.__doc__
            break
    return cls

このように適用することができます:

>>> @inheritdocstring
... class SubClassWithoutDocstring(ParentWithDocstring):
...     pass
... 
>>> SubClassWithoutDocstring.__doc__
'Parent docstring'
于 2012-12-18T16:40:16.987 に答える
2

最も簡単な方法は、クラス変数として割り当てることです。

class ParentWithDocstring(object):
    """Parent docstring"""
    pass

class SubClassWithoutDoctring(ParentWithDocstring):
    __doc__ = ParentWithDocstring.__doc__

parent = ParentWithDocstring()
print parent.__doc__  # Prints "Parent docstring".
subclass = SubClassWithoutDoctring()
assert subclass.__doc__ == parent.__doc__

残念ながら手動ですが、簡単です。ちなみに、文字列の書式設定は通常の方法では機能しませんが、同じ方法で機能します。

class A(object):
    _validTypes = (str, int)
    __doc__ = """A accepts the following types: %s""" % str(_validTypes)

A accepts the following types: (<type 'str'>, <type 'int'>)
于 2013-07-25T16:02:15.680 に答える
2

この特定のケースでは、メソッドをオーバーライドすることで、REST フレームワークがエンドポイントに使用する名前を決定する方法をオーバーライドすることもできます.get_name()

その方法を取る場合は、ビューの基本クラスのセットを定義し、単純な mixin クラスを使用してすべての基本ビューのメソッドをオーバーライドしたいと思うでしょう。

例えば:

class GetNameMixin(object):
    def get_name(self):
        # Your docstring-or-ancestor-docstring code here

class ListAPIView(GetNameMixin, generics.ListAPIView):
    pass

class RetrieveAPIView(GetNameMixin, generics.RetrieveAPIView):
    pass

また、get_nameメソッドは非公開と見なされ、将来のある時点で変更される可能性があるため、アップグレード時にリリース ノートに変更がないかどうかを確認する必要があることに注意してください。

于 2012-12-18T17:49:01.537 に答える
0

を使用して行うこともできます@property

class ParentWithDocstring(object):
    """Parent docstring"""
    pass

class SubClassWithoutDocstring(ParentWithDocstring):
    @property
    def __doc__(self):
        return None

class SubClassWithCustomDocstring(ParentWithDocstring):
    def __init__(self, docstring, *args, **kwargs):
        super(SubClassWithCustomDocstring, self).__init__(*args, **kwargs)
        self.docstring = docstring
    @property
    def __doc__(self):
        return self.docstring

>>> parent = ParentWithDocstring()
>>> print parent.__doc__  # Prints "Parent docstring".
Parent docstring
>>> subclass = SubClassWithoutDocstring()
>>> print subclass.__doc__  # Prints "None"
None
>>> subclass = SubClassWithCustomDocstring('foobar')
>>> print subclass.__doc__  # Prints "foobar"
foobar

docstring を上書きすることもできます。

class SubClassOverwriteDocstring(ParentWithDocstring):
    """Original docstring"""
    def __init__(self, docstring, *args, **kwargs):
        super(SubClassOverwriteDocstring, self).__init__(*args, **kwargs)
        self.docstring = docstring
    @property
    def __doc__(self):
        return self.docstring

>>> subclass = SubClassOverwriteDocstring('new docstring')
>>> print subclass.__doc__  # Prints "new docstring"
new docstring

1 つの注意点として、プロパティは明らかに他のクラスに継承できません。docstring を上書きする各クラスにプロパティを追加する必要があります。

class SubClassBrokenDocstring(SubClassOverwriteDocstring):
    """Broken docstring"""
    def __init__(self, docstring, *args, **kwargs):
        super(SubClassBrokenDocstring, self).__init__(docstring, *args, **kwargs)

>>> subclass = SubClassBrokenDocstring("doesn't work")
>>> print subclass.__doc__  # Prints "Broken docstring"
Broken docstring

残念!しかし、メタクラスのことを行うよりも間違いなく簡単です!

于 2014-01-17T01:03:04.907 に答える