Pythonでクラス属性を文書化する方法は?[閉まっている]


115

私は、その属性がパブリックにアクセスできるように意図され、特定のインスタンス化で時々オーバーライドされるだけの軽量クラスを書いています。Python属性には、クラス属性のdocstringや、あらゆる種類の属性を作成するための規定はありません。これらの属性を文書化するために期待され、サポートされている方法は何ですか?現在、私はこのようなことをしています:

class Albatross(object):
    """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
    """

    flight_speed = 691
    __doc__ += """
        flight_speed (691)
          The maximum speed that such a bird can attain.
    """

    nesting_grounds = "Raymond Luxury-Yacht"
    __doc__ += """
        nesting_grounds ("Raymond Luxury-Yacht")
          The locale where these birds congregate to reproduce.
    """

    def __init__(self, **keyargs):
        """Initialize the Albatross from the keyword arguments."""
        self.__dict__.update(keyargs)

これにより、初期の標準のdocstringセクションを含むクラスのdocstringと、への拡張割り当てによって各属性に追加された行が生成されます__doc__

このスタイルはdocstringスタイルガイドラインで明示的に禁止されているようではありませんが、オプションとしても言及されていません。ここでの利点は、表示可能なクラスdocstringを作成しながら、属性をその定義と一緒に文書化する方法を提供し、docstringからの情報を繰り返すコメントを書く必要を回避することです。実際に属性を2回記述しなければならないのは、いらいらしています。少なくともデフォルト値の重複を避けるために、docstringの値の文字列表現を使用することを検討しています。

これはアドホックコミュニティの慣習の重大な違反ですか?大丈夫ですか?もっと良い方法はありますか?たとえば、属性の値とdocstringを含むディクショナリを作成し、その内容をクラスに追加して__dict__、クラス宣言の最後の方にdocstring を追加することができます。これにより、属性の名前と値を2回入力する必要がなくなります。 編集:この最後のアイデアは、実際には不可能であると私は思います。少なくとも、データからクラス全体を動的に構築することなしにはそうではありません。

私はPythonにかなり慣れていないので、コーディングスタイルの詳細を検討しています。したがって、無関係な批評も歓迎します。


Djangoモデルの属性を文書化する方法を探しているなら、これが役に立つかもしれません:djangosnippets.org/snippets/2533
Michael Scheper

3
Pythonでフィールドとプロパティをドキュメント化する方法の重複別のソリューションを保持しています。
bufh

1
これが意見に基づく理由がわかりません。Pythonは、PEPで受け入れ可能な規則を具体的に文書化しています。適切にフォーマットされたドキュメントを抽出するさまざまなPythonソースツールがあります。実際、Pythonは実際にはよく知られていないPEP 257attribute doc string言及されており、OPの質問に答える可能性があり、いくつかのソースツールでサポートされているとは言い難いようです。これは意見ではありません。それは事実であり、言語の一部であり、OPが望むものとほぼ同じです。
NeilG

回答:


83

混乱を避けるために、Python ではプロパティという用語に特定の意味があります。あなたが話しているのは、クラス属性と呼ばれるものです。それらは常にクラスを通じて実行されるため、クラスのドキュメント文字列内にドキュメント化することは理にかなっています。このようなもの:

class Albatross(object):
    """A bird with a flight speed exceeding that of an unladen swallow.

    Attributes:
        flight_speed     The maximum speed that such a bird can attain.
        nesting_grounds  The locale where these birds congregate to reproduce.
    """
    flight_speed = 691
    nesting_grounds = "Throatwarbler Man Grove"

それはあなたの例のアプローチよりもはるかに目に優しいと思います。属性値のコピーをドキュメント文字列に表示したい場合は、各属性の説明の横または下に配置します。

Pythonでは、doc文字列はドキュメント化されたオブジェクトの実際のメンバーであり、単なるソースコードの注釈ではないことに注意してください。クラス属性変数はオブジェクト自体ではなくオブジェクトへの参照であるため、独自のdoc文字列を保持する方法がありません。「実際にここにあるもの」ではなく「何をここに置くべきか」を説明するために、参照にdoc文字列を使用することはできると思いますが、それを含むクラスのdoc文字列でそれを行うのは簡単です。


ほとんどの場合、これは問題ないと思います。属性(用語の訂正に感謝)は簡潔に宣言されているため、クラス宣言の最初にグループ化するだけで、前後に反転することは実用的ではありません。ドキュメントとデフォルト値}または{ドキュメントおよび/またはデフォルト値の両方のインスタンスを更新}。
2010年

1
また、私の例では、属性のドキュメントがクラスのdocstringに表示されることに注意してください。実際には、属性自体のdocstringsにドキュメンテーションを配置したいと思いますが、これはほとんどの組み込み型では機能しません。
2010年

はい、私の最初のアイデアは、例えばを宣言するだけでしたflight_speed = 691; flight_speed.__doc__ = "blah blah"。これはあなたが編集で言及していることだと思います。残念ながら、これは(intその例のように)(ほとんど?)組み込み型のインスタンス化には機能しません。ユーザー定義型のインスタンス化には機能します。===========クラス/モジュール属性のドキュメント文字列の追加を提案するPEP(申し訳ありませんが、番号を忘れました)が実際にありましたが、明確にする方法を理解できなかったため拒否されましたdocstringが先行する属性用か後続の属性用か。
2010

2
それで、それらがインスタンス属性である場合はどうなりますか?それでもクラスdocstringまたは何に文書化しますか?
n611x007 2015年

1
@intuitedこれはPEPでしたか?legacy.python.org/dev/peps/pep-0224
taz

30

セクションで、ドキュメンテーション文字列の表記:あなたはPEP257を引用docstringとは何か、それが記載されています。

Pythonコードの他の場所にある文字列リテラルも、ドキュメントとして機能する場合があります。これらはPythonバイトコードコンパイラでは認識されず、ランタイムオブジェクト属性としてアクセスできません(つまり、__ doc__に割り当てられていません)。ただし、ソフトウェアツールによって2種類の追加のdocstringが抽出される場合があります。

モジュール、クラス、または__init__メソッドの最上位レベルでの単純な割り当ての直後に発生する文字列リテラルは、「属性docstrings」と呼ばれます。

これについては、PEP 258:Attribute docstringsで詳しく説明しています。上記で説明したように、属性は__doc__を所有できるオブジェクトではないため、help()またはpydocには表示されません。これらのdocstringは、生成されたドキュメントにのみ使用できます。

これらは、ディレクティブautoattributeとともにSphinxで使用されます。

Sphinxは、割り当ての前の行のコメント、または割り当ての後に続く特別なコメント、または自動ドキュメント化される定義の後のdocstringを使用できます。


1
jedi-vimプラグインは、属性のdocstringも認識します。
Long Vu

1
これがいつ導入されたかはわかりませんが、Sphinx 1.2.2は生成されたドキュメントに属性docstringsを含めているようです。
jochen 2014

1
@jochenに感謝します。回答を更新します。
marcz

3
PEP 258は拒否されることに注意してください。拒否の通知には、「これは現在独立しているdocutilsの興味深い設計ドキュメントとして機能する可能性がありますが、標準ライブラリに含める予定はありません。」
のMichałŁazowik

13

この効果にプロパティを悪用する可能性があります。プロパティには、getter、setter、deleter、およびdocstringが含まれます。単純に、これは非常に冗長になります:

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """Docstring goes here."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

次に、Cxに属するdocstringがあります。

In [24]: print(C.x.__doc__)
Docstring goes here.

多くの属性に対してこれを行うのは面倒ですが、ヘルパー関数mypropを想定できます。

def myprop(x, doc):
    def getx(self):
        return getattr(self, '_' + x)

    def setx(self, val):
        setattr(self, '_' + x, val)

    def delx(self):
        delattr(self, '_' + x)

    return property(getx, setx, delx, doc)

class C:
    a = myprop("a", "Hi, I'm A!")
    b = myprop("b", "Hi, I'm B!")

In [44]: c = C()

In [46]: c.b = 42

In [47]: c.b
Out[47]: 42

In [49]: print(C.b.__doc__)
Hi, I'm B!

次に、Pythonをインタラクティブに呼び出す helpなります。

Help on class C in module __main__:

class C
 |  Data descriptors defined here:
 |  
 |  a
 |      Hi, I'm A!
 |  
 |  b
 |      Hi, I'm B!

それはあなたが望んでいるものとほぼ同じだと思います。

編集myprop内部名は問題ではないので、最初の引数を渡す必要をまったく回避できる可能性があることがわかりました。の後続の呼び出しmypropが何らかの形で相互に通信できる場合、それは長くありそうもない内部属性名を自動的に決定できます。これを実装する方法は確かにありますが、それが価値があるかどうかはわかりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.