Pythonでは、メソッドではなく関数をいつ使用すればよいですか?


118

PythonのZenは、物事を行う方法は1つだけである必要があると述べています。

ささいな例であるChessBoardオブジェクトを見てみましょう。理事会でキングの合法的な動きをすべて利用できるようにする方法が必要だとしましょう。ChessBoard.get_king_moves()またはget_king_moves(chess_board)を記述しますか?

これが私が調べたいくつかの関連する質問です:

私が得た答えはほとんど決定的ではありませんでした:

なぜPythonは一部の機能(list.index()など)にメソッドを使用し、他の機能(たとえばlen(list))に関数を使用するのですか?

主な理由は歴史です。関数は、タイプのグループに対して一般的であり、メソッドをまったく持たないオブジェクト(タプルなど)でも機能することを目的とした操作に使用されました。Pythonの関数機能(map()、apply()など)を使用するときに、アモルファスオブジェクトのコレクションに簡単に適用できる関数があると便利です。

実際、組み込み関数としてlen()、max()、min()を実装する方が、実際には、各型のメソッドとして実装するよりもコードが少なくて済みます。個々のケースについて疑問を投げかけることはできますが、それはPythonの一部であり、そのような根本的な変更を今行うには遅すぎます。関数は、大規模なコード破損を回避するために残しておく必要があります。

興味深いことですが、上記では実際に採用する戦略についてはあまり触れていません。

これが理由の1つです。カスタムメソッドを使用すると、開発者はgetLength()、length()、getlength()などの異なるメソッド名を自由に選択できます。Pythonは厳密な命名を強制するため、共通の関数len()を使用できます。

もう少し面白い。私の見解では、関数はある意味では、インターフェースのPythonicバージョンです。

最後に、グイド自身から

能力/インターフェースについて話すと、「ローグ」の特別なメソッド名のいくつかについて考えさせられました。言語リファレンスでは、「クラスは、特殊な名前でメソッドを定義することにより、特殊な構文(算術演算や添え字やスライスなど)によって呼び出される特定の演算を実装できる」と述べています。しかし、構文のサポートではなく、組み込み関数の利点のために提供されているように見える、__len__またはのような特別な名前を持つこれらすべてのメソッドが__unicode__あります。それはので、おそらく、インターフェイスベースのPythonで、これらの方法は、ABCの定期的な名前のメソッドに変わるでしょう __len__になるだろう

class container:
  ...
  def len(self):
    raise NotImplemented

それについてもう少し考えますが、すべての構文操作が特定のABCで適切な通常の名前のメソッドを呼び出さ<object.lessthancomparable.lessthanないのはなぜかわかりません」は、例えば、あろうおそらく呼び出し「(あるいは「」「)。したがって、もう1つの利点は、Pythonをこの複雑な名前の奇妙さから引き離すことができることです。これは、HCIの改善のようです。

うーん。同意するかどうかはわかりません(図:-)。

最初に説明したいのが「Pythonの理論的根拠」です。

まず、HCIの理由から、x.len()ではなくlen(x)を選択しました(def __len__()かなり後になりました)。実際には2つの理由があり、どちらもHCIです。

(a)一部の操作では、接頭表記は単にpostfixよりも読みやすくなります-接頭辞(およびインフィックス!)の操作は数学において長い伝統があり、ビジュアルが問題について数学者が考える助けとなる表記法を好みます。式を次のようx*(a+b)に書き換える簡単さを比較してくださいx*a + x*b生OO表記を使用して同じことをやっての不器用さにします。

(b)何かの長さを要求していることlen(x)知っているというコードを読んだとき。これは2つのことを教えてくれます。結果は整数であり、引数はなんらかのコンテナです。逆に、私がを読んだときx.len()、それがxインターフェイスを実装しているコンテナ、または標準を持つクラスから継承しているコンテナの一種であることをすでに知っている必要がありますlen()。マッピングを実装していないクラスにget()or keys() メソッドがある場合、またはファイルではないものにwrite()ください。

同じことを別の言い方をすると、「len」は組み込みの操作と見なされ ます。それを失うのは嫌だ。あなたがそれを意味していたのかどうかははっきりしませんが、 'def len(self):...'は通常の方法に降格したいようです。私はそのことを強く-1としています。

説明することを約束したPythonの理論的根拠の2番目のビットは、__special__単に見るだけでなく、 特別な方法を選んだ理由specialです。クラスがオーバーライドする可能性のある多くの操作、一部の標準(__add__またはまたは__getitem__)、一部の標準ではない(たとえば、ピクルス__reduce__は長い間Cコードでまったくサポートされていなかった)を予期していました。これらの特別な操作で通常のメソッド名を使用したくありませんでした。そのため、既存のクラス、またはすべての特別なメソッドのエンサイクロペディックメモリを持たないユーザーが作成したクラスは、実装するつもりのない操作を誤って定義してしまう可能性があります。 、悲惨な結果をもたらす可能性があります。IvanKrstićはこれをより簡潔に彼のメッセージで説明しました。メッセージは私がこれをすべて書いた後に届きました。

---Guido van Rossum(ホームページ:http ://www.python.org/~guido/ )

これについての私の理解は、特定のケースでは、プレフィックス表記がより意味をなす(つまり、Duck.quackがquack(Duck)よりも言語の観点から理解できる)ことであり、関数も「インターフェース」を許可します。

そのような場合、私の推測は、Guidoの最初の点のみに基づいてget_king_movesを実装することです。しかし、それでも、同様のプッシュメソッドとポップメソッドを使用してスタッククラスとキュークラスを実装するなど、関数またはメソッドのどちらであるかについて、未解決の問題が数多く残っています。(私は本当にプッシュポップインターフェイスに信号を送りたいので、ここでは関数を推測します)

TLDR:関数とメソッドのどちらを使用するかを決定するための戦略を誰かが説明できるか?


2
ええ、私はいつもそれを全く恣意的だと​​思っていました。ダックタイピングは暗黙の「インターフェース」を可能にします。自分が持っていてX.frobX.__frob__、独立していても、それほど大きな違いはありませんfrob
Cat Plus Plus

2
私はほとんどあなたに同意しますが、原則としてあなたの答えはPythonicではありません。「あいまいさに直面して、推測する誘惑を拒否してください。」(もちろん、締め切りはこれを変更しますが、私は楽しみ/自己改善のためにこれを行っています。)
Ceasar Bautista

これは私がpythonについて好きではないことの1つです。intのようなキャストタイピングを文字列に強制する場合は、それをメソッドにしてください。括弧で囲む必要があり、時間がかかります。
マット

1
これが私がPythonを好きではない最も重要な理由です。何かを実現したいときに、関数を探す必要があるのか​​、メソッドを探す必要があるのか​​わからないのです。さらに、ベクトルやデータフレームなどの新しいデータタイプを含む追加のライブラリを使用すると、さらに複雑になります。
vonjd 2017年

1
「Zen of Pythonは、そうでない場合を除いて、実行する方法は1つだけであると述べています」
18

回答:


84

私の一般的なルールはこれです- 操作はオブジェクトに対して行われますか、それともオブジェクトによって行われますか?

オブジェクトによって行われる場合は、メンバー操作である必要があります。他にも適用できる場合、またはオブジェクトに対して他の何かによって行われる場合は、関数(または他の何かのメンバー)である必要があります。

プログラミングを導入するとき、車などの現実世界のオブジェクトの観点からオブジェクトを記述することは(実装は正しくありませんが)伝統的です。あなたはアヒルに言及しているので、それで行こう。

class duck: 
    def __init__(self):pass
    def eat(self, o): pass 
    def crap(self) : pass
    def die(self)
    ....

「オブジェクトは実在のもの」の類推の文脈では、オブジェクトが実行できるすべてのことに対してクラスメソッドを追加することは「正しい」ことです。だから、アヒルを殺したいのですが、アヒルに.kill()を追加しますか?いいえ...私が知る限り、動物は自殺しません。したがって、アヒルを殺したい場合は、次のようにします。

def kill(o):
    if isinstance(o, duck):
        o.die()
    elif isinstance(o, dog):
        print "WHY????"
        o.die()
    elif isinstance(o, nyancat):
        raise Exception("NYAN "*9001)
    else:
       print "can't kill it."

このアナロジーから離れて、なぜメソッドとクラスを使用するのですか?データを格納し、うまくいけば、将来的に再利用可能で拡張可能なようにコードを構造化したいからです。これは、OO設計にとって非常に重要なカプセル化の概念をもたらします。

カプセル化プリンシパルとは、まさにこれです。デザイナーとして、実装やクラスの内部について、ユーザーや他の開発者が必ずしもアクセスする必要のないものをすべて隠す必要があります。クラスのインスタンスを扱うので、これは「このインスタンスでどの操作が重要か」に減少します。操作がインスタンス固有でない場合は、メンバー関数であってはなりません。

TL; DR:@ブライアンが言ったこと インスタンスで動作し、クラスインスタンスの内部にあるデータにアクセスする必要がある場合は、メンバー関数である必要があります。


つまり、非メンバー関数は不変オブジェクトを操作し、可変オブジェクトはメンバー関数を使用しますか?(または、これは一般化が厳しすぎるのですか?これは、不変の型に状態がないためだけに特定の動作で発生します。)
Ceasar Bautista

1
厳密なOOPの観点からは、それは公平だと思います。Pythonにはパブリック変数とプライベート変数(名前が__で始まる変数)があり、Javaとは異なり、アクセス保護が保​​証されていないため、単純に許容言語について議論しているだけなので、絶対的なものはありません。ただし、Javaのような許容度の低い言語では、getFoo()およびsetFoo()関数が標準であるため、不変性は絶対的なものではありません。クライアントコードは、メンバーへの割り当てを許可されていません。
2011年

1
@シーザーそれは本当ではない。不変オブジェクトには状態があります。それ以外の場合は、整数を他の整数と区別することはできません。不変オブジェクトは状態を変更しません。一般に、これにより、すべての州が公になることの問題がはるかに少なくなります。そしてその設定では、関数を使用して不変のすべてのパブリックオブジェクトを有意義に操作する方がはるかに簡単です。メソッドであることの「特権」はありません。
ベン

1
@CeasarBautistaええ、ベンにはポイントがあります。コード設計には、1)設計されていない、2)OOP、および3)機能的な3つの「主要な」学校があります。機能的なスタイルでは、状態はまったくありません。これは私が設計したほとんどのpythonコードを見る方法であり、入力を受け取り、副作用がほとんどない出力を生成します。OOP のポイントは、すべてに状態があることです。クラスは状態のコンテナであり、したがって「メンバー」関数は、呼び出されるたびにクラスで定義された状態をロードする、状態依存および副作用ベースのコードです。Pythonは機能的に傾いている傾向があるため、非メンバーコードが優先されます。
2011年

1
食べる(自己)、がらくた(自己)、死ぬ(自己)。hahahaha
Wapiti

27

次の場合にクラスを使用します。

1)実装の詳細から呼び出しコードを分離する- 抽象化カプセル化を利用する

2)他のオブジェクトの代わりになりたいとき- 多態性を利用する

3)同様のオブジェクトのコードを再利用したい場合- 継承を利用する

組み込みのlenreprなど、さまざまなオブジェクトタイプで意味のある呼び出しに関数を使用する関数関数は多くの種類のオブジェクトに適用されます。

そうは言っても、選択は時々好みの問題に帰着します。通常の通話で最も便利で読みやすいものについて考えます。たとえば、どちらが良いでしょう(x.sin()**2 + y.cos()**2).sqrt()sqrt(sin(x)**2 + cos(y)**2)


8

簡単な経験則を次に示します。コードがオブジェクトの単一のインスタンスに作用する場合は、メソッドを使用します。さらに良い方法は、関数として記述する理由がない限り、メソッドを使用することです。

具体的な例では、次のようにします。

chessboard = Chessboard()
...
chessboard.get_king_moves()

考えすぎないでください。「これをメソッドにするのは意味がありません」と自分が言うポイントが来るまで、常にメソッドを使用します。その場合、関数を作成できます。


2
関数ではなくメソッドをデフォルトにする理由を説明できますか?(そして、スタックとキュー/ポップとプッシュのメソッドの場合にそのルールがまだ意味があるかどうかを説明できますか?)
Ceasar Bautista

11
その経験則は意味をなさない。標準ライブラリ自体は、クラスと関数のどちらを使用するかについてのガイドになります。 heapqmathは、通常のpythonオブジェクト(浮動小数点数とリスト)を操作し、ランダムモジュールのように複雑な状態を維持する必要がないため、関数です。
レイモンドヘッティンガー2011年

1
標準ライブラリが違反しているため、ルールは意味をなさないと言っていますか?その結論は意味をなさないと思います。まず、経験則はそれだけです。絶対的な規則ではありません。さらに、標準ライブラリの大部分はその経験則に従ってます。OPはいくつかの簡単なガイドラインを探していましたが、私が始めたばかりの人には私のアドバイスは完全に良いと思います。しかし、私はあなたの視点に感謝します。
Bryan Oakley

さて、STLにはそうするべきいくつかの正当な理由があります。数学とcoの場合、クラスがないため、クラスを作成しても意味がありません(他の言語では、静的関数のみを含むクラスです)。言うようにいくつかの異なるコンテナーで動作するものについてlen()は、デザインも理にかなっていると主張することもできますが、個人的には機能もそれほど悪くはないと思います- len()常に返す必要がある別の規則があります整数(ただし、バックコンプのすべての追加の問題があるので、Pythonでもそれを主張しません)
Voo

-1この回答は完全に任意です。つまり、XがYよりも優れていると想定して、XがYよりも優れていることを実証しようとしています。
gented

7

私はたいてい人のようなものを思い浮かべます。

の属性は、人の名前、身長、靴のサイズなどです。

メソッド機能は、その人が実行できる操作です。

この一人の特定の人に固有の何かを必要とせずに(そしてこの一人の特定の人に何も変更せずに)、操作が老人だけで実行できる場合、それは機能ですあり、そのように記述されるべきです。

操作が人に作用している場合(例:食べる、歩くなど)、または関与するためにこの人に固有の何かが必要な場合(ダンス、本を書くなど)、それはメソッドである必要があります

もちろん、これを作業中の特定のオブジェクトに変換することは必ずしも簡単なことではありませんが、それを考える良い方法だと思います。


しかし、あなたは老人の身長を測定することができるので、そのロジックによってそれはそうあるべきでheight(person)はありませんperson.heightか?
エンドリス2014

@endolith確かに、高さを取得するために特別な作業を実行する必要がないため、高さの方が属性として適していると思います。数値を取得する関数を書くことは、ジャンプするための不必要なフープのようです。
Thriveth 2014

@endolith一方、その人が成長して身長を変える子供である場合、メソッドではなく、メソッドにその機能を任せるのは明らかです。
Thriveth 2014

これは当てはまりません。あなたが持っている場合はどうなりますare_married(person1, person2)か?このクエリは非常に一般的であるため、メソッドではなく関数にする必要があります。
ピティコス2014

@Pithikosもちろんですが、これには、1つのオブジェクト(人物)に対して実行される属性やアクションだけでなく、2つのオブジェクト間の関係も含まれます。
Thriveth 2014

4

一般的に私はいくつかのための機能の論理的なセットを実装するクラスを使用する事をそれは私のプログラムの残りの部分で、私はについて推論することができますので、その実装を構成するすべての少しの懸念を気にすることはありません、。

通常、「もので何ができるか」というコアの抽象概念の一部であるものはすべて、メソッドでなければなりません。これは、一般的に変えることができるすべて含ま事を内部データ状態は通常、あなたが何ができるか」の論理的な考え方のプライベートと一部ではないと考えられているとして、」。

あなたがより高いレベルの操作に来るとき、特にそれらが複数のものを含む場合、内部への特別なアクセスを必要とせずに物事のパブリックアブストラクションから構築できる場合、それらは通常、最も自然に関数として表現されます。他のオブジェクトのreメソッド)。これには、内部構造を完全に書き直すことにしたときに、(インターフェースを変更せずに)作品を、私はちょうどそれらの方法の観点で書かれたすべての外部関数を書き換える方法の小さなコアセットを持っており、ちょうど動作します。クラスXで実行するすべての操作はクラスXのメソッドであると主張すると、クラスが過度に複雑になります。

それは私が書いているコードにもよりますが。一部のプログラムでは、それらの相互作用がプログラムの動作を引き起こすオブジェクトのコレクションとしてモデル化します。ここで最も重要な機能は単一のオブジェクトに密接に結合されているため、ユーティリティ関数の分散によりメソッドに実装されます。他のプログラムにとって最も重要なものは、データを操作する一連の関数であり、クラスは、関数によって操作される自然な「アヒル型」を実装するためにのみ使用されます。


0

「あいまいに直面して、推測する誘惑を拒否する」と言うかもしれません。

しかし、それは推測でさえありません。あなたは両方のアプローチの結果があなたの問題を解決するという点で同じであることを絶対に確信しています。

目標を達成するための複数の方法があるのは良いことだと思います。他のユーザーが既に行ったように、言語に関して「より良い味わい」/より直感的に感じる方を採用するように控えめに言っておきます。

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