web-dev-qa-db-ja.com

コレクションを引数として渡すか、新しいコレクションを返す必要がある場合のガイドラインはありますか?

次のメソッドがあるとします。

_def read(file: str) -> List[str]:
    temp = []
    with open(file) as f_obj:
        for line in f_obj:
            temp.append(line)
    return temp


def append_items_to_list(folders : List[str], file: str):
    with open(file) as f_obj:
        for line in f_obj:
            folders.append(line)


def main():

    source: str = "source.txt"

    directories : List[str] = read(file=source)
    print(len(directories))     

    folderList : List[str] = []
    append_items_to_list(folders=folderList, file=source)
    print(len(folderList))

if __name__ == '__main__':
    main()
_

どちらのメソッドもファイルから読み取り、Listにアイテムを追加します。最初のメソッドは一時的なListを作成して返しますが、2番目のメソッドはListをパラメーターとして受け入れ、それに追加します。

どちらが望ましいかを示すガイドラインはありますか?どちらかについて心配する必要があるとは思わない根本的な問題はありますか?

新しいListを作成する必要がある理由を確認する必要があると思います。この場合、新しいListを作成する必要はありません。2番目の方法を使用できます。

説明

一般的な答えは新しいListを返すことですが、問題をもう少し絞り込みました。

_def do_work(source: Path, destination: Path):
    # do some work with the folders.

def create_list_of_empty_folders(folders : List[str], folder: Path):
    # Check if folder is empty, if so, append it to the List.

def cleanup(folders_to_remove: List[Path]):
    # Remove all folders in the List from the HD.


def main():

    portableHD = Path("Path\\to\\HD")
    USB = Path("Path\\to\\USB")

    empty_folders : List[Path] = []

    for folder in portableHD.iterdir():
        do_work(source=folder, destination=USB)
        create_list_of_empty_folders(folders=empty_folders, folder=folder)

     # Outside of the for loop we can safely remove the empty folders.
    cleanup(folders_to_remove=empty_folders)

if __name__ == '__main__':
    main()
_

このコンテキストでは、Listfolderを関数に渡す以外に選択肢はありません。明らかに、関数を使用せずにforループでコードを書き出すことができますが、クリーンで保守可能なコードを書きたいとしましょう。

最初の例では、実際に関数を使用する必要はありません。データを読み取り、Listに直接mainメソッドで追加できます。

_def main():

    file: str = "source.txt"

    folders : List[str] = []

    with open(file) as f_obj:
        for line in f_obj:
            folders.append(line)

if __name__ == '__main__':
    main()
_

しかし、これらの各フォルダーでさらに多くの作業を行う必要があると想定して、コードをリファクタリングしてより小さなメソッドを作成します。なぜ関数から一時的なListを返すのが望ましいのでしょうか。

関数は本当に、呼び出し元の目的が追加であることを知っている必要がありますか?

呼び出し元がListに追加したいことを関数が認識できないのはなぜですか?結局のところ、コードをより小さな関数に分解し、そのうちの1つをListに追加しました。メソッドの名前をappend(folders : List[str], file: str)に変更したとしても、関数のパラメーターは追加したいものを示します。

2
user347963

そのような普遍的なガイドラインはありません。

言語イディオム、言語機能、パフォーマンス、プログラミングパラダイム(関数指向など)などの言語固有の側面を無視すると、いくつかの経験則があります。

操作のsemanticに応じて、実装する関数の最も広い再利用の可能性を検討します。

  • 他の誰かが所有するリストを変更することについてですか?この場合、リストを入出力引数として渡すことになります。
  • それは誰かが所有するリストを強化または変換することについてですが、変更が必ずしも達成される目標ではない場合ですか?この場合、リストを入力引数として渡し、新しい結果リストを返します。
  • 新しいリストを作成することについてですか?入力リストを取得せずに出力リストを返すだけです。

あなたの例では、それはフォルダ内のファイルの新しいリストを作成することです。関数は本当に、呼び出し元の目的が追加であることを知っている必要がありますか?それとも、リストの所有権を考慮せずに、通常はうまく機能するでしょうか?新しいリストを返して、発信者に何をするかを決定させます。

説明

これらは経験則ですが、作成するすべてのプログラムについて、設計上の決定はあなた次第です。

特定のユースケースでは、大きな関数を小さな関数に分割することにしました。これは設計上の決定です。つまり、原則として最初のケースです。リストは、全体的な処理を担当する呼び出し元によって所有され、関数はその一部のみを実行します。

何も問題はなく、ここで止めることができます。次に、再利用の可能性についてのポイントが来ます。個人的には、他の場合にもフォルダのリストの多くの用途を見ることができます。これが、デザインの決定を修正し、関数をリファクタリングして新しいリストを作成し、呼び出し側に追加を行わせる理由です。再利用の機会が見当たらない場合は、ぜひお試しください。

リストについてさらに多くのことがある場合でも、私はそれを一般的に保ち、呼び出し元がリストの要素に関数を適用できるようにします(関数は、名前の変更、日付のタッチ、移動、印刷、 ...)。しかし、繰り返しますが、おそらく私が見る可能性はあなたの場合には関係ありません。最終的に選択はあなた次第です。他に理由がある場合は、いかなる規則もあなたに選択を指示することはありません。

2
Christophe

私がお勧めするガイドラインは次のとおりです。

ユーザーに不要な制限をかけないでください。

リストをパラメーターとして受け取る場合、ユーザーが提供するアイテムよりも多くのアイテムを必要としていない場合でも、ユーザーはリストを作成して渡す必要があります。それにより、コードは他の場合よりも使用するのが面倒になります。

1
Kyralessa