複雑なAPI関数に対する小さな変更を文書化する方法は?


8

いくつかのライブラリからインポートされた複雑なAPI関数があるとします。

def complex_api_function(
        number, <lots of positional arguments>,
        <lots of keyword arguments>):
    '''really long docstring'''
    # lots of code

小さな変更を加えるために、その関数の周りに簡単なラッパーを書きたいと思います。たとえば、最初の引数を文字列として渡すことができるはずです。これを文書化する方法は?私は次のオプションを検討しました:

オプション1:

def my_complex_api_function(number_or_str, *args, **kwargs):
    '''
    Do something complex.

    Like `complex_api_function`, but first argument can be a string.

    Parameters
    ----------
    number_or_str : int or float or str
        Can be a number or a string that can be interpreted as a float.
        <copy paste description from complex_api_function docstring>
    *args
        Positional arguments passed to `complex_api_function`.
    **kwargs
        Keyword arguments passed to `complex_api_function`.

    Returns
    -------
    <copy paste from complex_api_function docstring>

    Examples
    --------
    <example where first argument is a string, e.g. '-5.0'>

    '''
    return complex_api_function(float(number_or_str), *args, **kwargs)

欠点:およびcomplex_api_functionに関する情報を取得するには、ユーザーはのドキュメントを参照する必要が*argsあり**kwargsます。コピーからセクションを貼り付けてcomplex_api_function変更する場合は調整が必要です。

オプション2:

コピーアンドペーストcomplex_api_function(代わりに使用したのの署名*args**kwargs)とそのドキュメンテーション文字列を。docstringに小さな変更を加え、最初の引数も文字列にすることができることを述べます。例を追加します。

短所:詳細、変更時にcomplex_api_function変更する必要があります。

オプション3:

飾るmy_complex_api_functionfunctools.wraps(complex_api_function)

欠点:number文字列になる可能性のある情報はありません。


の変更点の詳細に依存しない答えを探していますmy_complex_api_function。この手順は、オリジナルを少し調整するだけで機能しcomplex_api_functionます。

回答:


3

私は次のようなものをお勧めします:

def my_complex_api_function(number_or_str, *args, **kwargs):
    """This function is a light wrapper to `complex_api_function`.
    It allows you to pass a string or a number, whereas `complex_api_function` requires a 
    number. See :ref:`complex_api_function` for more details.

    :param number_or_str: number or str to convert to a number and pass to `complex_api_function`.
    :param args: Arguments to pass to `complex_api_function`
    :param kwargs: Keyword arguments to pass to `complex_api_function`
    :return: Output of `complex_api_function`, called with passed parameters
    """

これは明確で簡潔です。ただし、sphinxなどのドキュメンテーションシステムを使用している場合は、関数を:ref:`bob`同様。


1
それはcomplex_api_function情報を複製するだけであるため、パラメーターの型が何を期待するかについては触れません(多分それらには複数のオプションもあるかもしれません)。おそらく、ラッパーのユーザーは元の関数にすでに精通しており、そうでない場合は常に元のドキュメントを参照することができます。とにかく、これは進むべき道だと思います。元の関数に追加されたものを文書化し、その新しい型を元の関数に変換する方法の詳細を提供するだけです(これらの詳細は重要かもしれません)。つまり、元の関数との互換性を保つために、その引数がどのように扱われるかです。
a_guest

1
これはリンクの良い点:ref:です。docstringにaの編集を追加しました。ただし、小さな APIの変更については、OPが要求しているように、ユーザーはより簡単に機能を比較できます。この場合、最小限の労力でエンドユーザーの利益が少し増える可能性があります。ドキュメントを読むとき、ほとんどの場合、6ページのドキュメントよりも12ページのドキュメントの方がわかりやすいでしょう。
Legorooj

5

あなたは、元のドキュメンテーション文字列の「専門」を自動化することができ補遺。たとえば、pydoc特別な属性__doc__を使用しています__doc__補遺で元の関数を自動的にオーバーライドするデコレータを書くことができます。

例えば:

def extend_docstring(original, addendum):
    def callable(func):
        func.__doc__ = original + addendum
        return func

    return callable


def complex_api_function(a, b, c):
    '''
    This is a very complex function.

    Parameters
    ----------
    a: int or float
        This is the argument A.
    b: ....
    '''
    print('do something')

@extend_docstring(
    complex_api_function.__doc__,
    '''
    Addendum
    --------
    Parameter a can also be a string
    '''
)
def my_complex_api_function(a, b, c):
    return complex_api_function(float(a), b, c)

または...

def extend_docstring(original):
    def callable(func):
        func.__doc__ = original + func.__doc__
        return func

    return callable


def complex_api_function(a, b, c):
    '''
    This is a very complex function.

    Parameters
    ----------
    a: int or float
        This is the argument A.
    b: ....
    '''
    print('do something')

@extend_docstring(complex_api_function.__doc__)
def my_complex_api_function(a, b, c):
    '''
    Addendum
    --------
    Parameter a can also be a string
    '''
    return complex_api_function(float(a), b, c)

pydocpydoc3 -w my_module.py)を実行すると、以下が生成されます:pydocによって生成されたhtmlのプレビュー

追加の注記:Python 3を使用している場合は、アノテーションを使用して関数パラメーターのタイプを文書化できます。ドキュメントだけでなく、多くの利点があります。例えば:

from typing import Union

def my_complex_api_function(number_or_str: Union[int, float, str], *args, **kwargs):

1
これには、重要な(新しい)情報が(おそらく非常に長い)doc文字列の最後に「非表示」になるという欠点があります。したがって、既存のドキュメント文字列に追加される唯一の貴重な情報である一方で、新機能の発見可能性は非常に低いです。さらに、元のドキュメント文字列の型宣言と競合します。つまり、ユーザーが拡張されたドキュメント文字列を見るa : floatと、一番上に表示され、strここでも使用できるという結論には至りません。偶然にドキュメントの最後までスクロールした場合にのみ、ドキュメントを見つけます。
a_guest

1
末尾ではなく先頭に追記を追加することもできます...ドキュメントの最初にある「改訂」ノートのように。
Raphael Medaer

1
もう1つの問題は、情報の重複(+フリーズ)です。このラッパーを出荷するパッケージをビルドし、依存関係をと指定するとしますcomplex_package >= 1.1.0。これで、パッケージをビルドするときに、の特定のバージョンを1つ使用する必要がありますcomplex_package。既にcomplex_package==1.5.0pypi が存在しcomplex_api_function、バージョンに新しいキーワード引数が追加されたとしましょう1.3.0。どちらの方法でも(1.1.0またはを使用して1.5.0)、ドキュメント内のユーザーのサブグループの情報が古くなっている/間違っています。同じことが、まだ公開されていない将来の変更にも当てはまります。
a_guest

-1

これがあなたが探しているものかどうかはわかりませんが、質問を完全に回避するのに役立ちます。

def first_as_num_or_str(func):
    '''Decorator allowing the first parameter of the given function to be a number or a string

    :param func: A function whose first argument is a number
    :return: `func`, but now the first argument is cast to a float
    ''' 
    def new_func(*args, **kwargs):
        func(float(args[0]), args[1:], kwargs)
    return new_func

wrapped_api_func = first_as_num_or_str(complex_api_function)

ありがとうございました。wrapped_api_funcdocstringがないため、ドキュメントの問題は解決されません。
actual_panda
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.