質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.75%

  • Python

    6841questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    5308questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

微妙に異なる引数を高階関数に渡したい時の適切な設計

解決済

回答 4

投稿

  • 評価
  • クリップ 2
  • VIEW 515

puroko3

score 51

def main(func):
    a = 1
    b = 2
    c = 3
    d = 4
    print(func(a, b, c, d))

def func1(a, b, c, _):
    return a + b + c

def func2(a, b, _, d):
    return a * b * d

main(func1)
main(func2)


このコードのように関数内で生成した変数を、関数オブジェクト毎に異なる引数を渡したい時の設計はどうするべきかと悩んでいます。

このコードでは全ての変数を引数に渡す方法を取っていますが、使わない変数までも受け取るのはやはり気持ち悪さを感じてしまいます...

関数の外の変数を、異なる引数として渡したいのであれば部分適用なりカリー化なりをすればいいと思うのですが、それと同じ感じで美しい方法はないのでしょうか?

これが最適であるのかどうか、もしくは他にいい方法があるのかどうか、わかる方いらしたらお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

率直に思うのは、こういうケースはそもそも高階関数を適用すべき状況なのか、ということです。

引数という関数の基本的なインターフェースが異なるのですから、高階関数で使おうとしても面倒くさいだけです。

「それでもやる」というのなら、私は次の方法を好みます。

def main(func):
    a = 1
    b = 2
    c = 3
    d = 4
    print(func(a, b, c, d))

def func1(a, b, c):
    return a + b + c

def func2(a, b, d):
    return a * b * d

main(lambda a, b, c, d: func1(a,b,c))
main(lambda a, b, c, d: func2(a,b,d))

別に何の解決にもなっていないのですが、少なくともfunc1func2に責任を押し付けないで済ませているので、多少マシな状況だと私は思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/03 02:47 編集

    回答ありがとうございます。
    高階関数さえ適用できれば、処理を使いまわせるのであれば、関数を分割して使いまわすよりも
    組み立てなくてもよかったりリファクタリングを最小限に抑えれて楽なので、引数が少し違う程度なら何とかならんかなと思った次第です。

    やはり根本的な解決は難しそうですか...
    ただ私的にもこちらの方がまだ好みです。

    キャンセル

+2

位置引数ではなくキーワード引数にすれば、少し気持ち悪さが軽減されるような気もします。

def main(func):
    params = {
        'a': 1, 'b': 2, 'c': 3, 'd': 4,
    }

    print(func(**params))

def func1(a, b, c, **kwargs):
    return a + b + c

def func2(a, b, d, **kwargs):
    return a * b * d

main(func1)
main(func2)

なお、部分適用にはfunctools.partialが利用出来ます。
利用がふさわしいかどうかは、実際のコードを見てみないとなんともわかりませんが。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/02 16:58

    なるほど、名前が一致しない奴は**kwargsに押し込まれる訳ですか。・・・いや、これはこれで、なんか・・・

    キャンセル

  • 2018/06/02 17:00 編集

    根本的な解決策にはなりませんね。『謎の引数』に蓋をしただけです。

    キャンセル

  • 2018/06/03 03:10

    回答ありがとうございます。
    面白い方法ですね。参考になります。
    ただどういうやり方でも、このケースはどうしても無駄が出来てしまう感じでしょうかね...
    まあダメ元でもう少しだけ回答受け付けようと思います。

    キャンセル

+1

意味の異なる変数に使っているのに、明示しないのはバグの温床の気がします。

def main(func):
    l = [1, 2, 3, 4]

    print(func(l))

def func1(l):
    return l[0] + l[1] + l[2]

def func2(l):
    return l[0] * l[1] * l[3]

main(func1)
main(func2)


func1とfunc2は引数を3つしかとらないのが自然な気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/05 02:36

    回答ありがとうございます。
    シンプルながらリストは思いつきませんでした。
    仰られているように、こういう時は高階関数は避けた方が良さそうですね...
    また機会があれば回答お願いします

    キャンセル

+1

すでにベストアンサーが出てしまっていますが、今回の場合は高階関数が適切でない、ということについて蛇足で回答させていただきます。

まず、関数の引数に関数内で使わないものは含めない方が良いので、func1やfunc2はhayataka2049様のコードのように

def func1(a, b, c):
    return a + b + c

def func2(a, b, d):
    return a * b * d


とするのが良いです。

そして、mainに関しては、高階関数をとらず、内部で2回printするとスッキリします。

def main():
    a = 1
    b = 2
    c = 3
    d = 4

    print(func1(a, b, c))
    print(func2(a, b, d))

main()

このように、func1とfunc2は計算をするだけの関数、mainはそれらの計算結果を出力するだけの関数、と小さくわかりやすい関数になります。

また、変数は必要なところで宣言するようにしましょう。(変数dはfunc1では使われていないですね)

def main():
    a = 1
    b = 2

    c = 3
    print(func1(a, b, c))

    d = 4
    print(func2(a, b, d))

こうすることで、変数dの影響する範囲を小さくでき、コードが読みやすくなります。

もし、質問者様の意図と異なる回答であったならば、申し訳ございません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/16 13:54

    回答ありがとうございます。
    やはり高階関数は、引数が一致する時のみに留めるべきですね...

    キャンセル

  • 2018/06/16 14:12

    高階関数は、共通する処理をくくり出したいときに使うと思います。
    この記事が、わかりやすいです。
    https://qiita.com/kwatch/items/03fd035a955235681577

    キャンセル

  • 2018/06/16 14:20

    わざわざありがとうございます!
    参考にさせていただきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.75%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python

    6841questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

  • Python 3.x

    5308questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。