Pythonでは、メソッドの代わりに関数を使うべきですか?

Zen of Pythonでは、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()を使うことができます。

やや面白い。私の考えでは、機能はある意味でPythonバージョンのインタフェースです。

最後に、 rel="nofollow noreferrer"> Guidoから

Talking about the Abilities/Interfaces made me think about some of our "rogue" special method names. In the Language Reference, it says, "A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names." But there are all these methods with special names like __len__ or __unicode__ which seem to be provided for the benefit of built-in functions, rather than for support of syntax. Presumably in an interface-based Python, these methods would turn into regularly-named methods on an ABC, so that __len__ would become

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

Though, thinking about it some more, I don't see why all syntactic operations wouldn't just invoke the appropriate normally-named method on a specific ABC. "<", for instance, would presumably invoke "object.lessthan" (or perhaps "comparable.lessthan"). So another benefit would be the ability to wean Python away from this mangled-name oddness, which seems to me an HCI improvement.

Hm. I'm not sure I agree (figure that :-).

There are two bits of "Python rationale" that I'd like to explain first.

First of all, I chose len(x) over x.len() for HCI reasons (def __len__() came much later). There are two intertwined reasons actually, both HCI:

(a) For some operations, prefix notation just reads better than postfix -- prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into x*a + x*b to the clumsiness of doing the same thing using a raw OO notation.

(b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn't a file has a write() method.

Saying the same thing in another way, I see 'len' as a built-in operation. I'd hate to lose that. I can't say for sure whether you meant that or not, but 'def len(self): ...' certainly sounds like you want to demote it to an ordinary method. I'm strongly -1 on that.

The second bit of Python rationale I promised to explain is the reason why I chose special methods to look __special__ and not merely special. I was anticipating lots of operations that classes might want to override, some standard (e.g. __add__ or __getitem__), some not so standard (e.g. pickle's __reduce__ for a long time had no support in C code at all). I didn't want these special operations to use ordinary method names, because then pre-existing classes, or classes written by users without an encyclopedic memory for all the special methods, would be liable to accidentally define operations they didn't mean to implement, with possibly disastrous consequences. Ivan Krstić explained this more concise in his message, which arrived after I'd written all this up.

-- --Guido van Rossum (home page: http://www.python.org/~guido/)

これについての私の理解は、ある場合には接頭辞表記がちょうど理にかなっているということです(つまり、Duck.quackは言語的観点からquack(Duck)よりも理にかなっています)。

そのような場合、私の推測は、Guidoの最初の点にのみ基づいてget_king_movesを実装することです。しかし、これは、スタックやキュークラスを同様のプッシュ/ポップメソッドで実装することは、関数やメソッドでなければいけないということに関して、未だに多くの未解決の問題を残しています。 (私は本当にプッシュポップインターフェースを知らせたいので、ここでは関数を推測します)

TLDR:誰かが、どのような方法で関数を使うべきかを決める戦略を説明することはできますか?

88
私はあなたにほとんど同意しますが、原則として答えはPythonicではありません。 「あいまいさに直面して、推測を拒否する」と思い起こしてください。 (もちろん、期限はこれを変えるだろうが、私は楽しい/自己改善のためにこれをやっている。)
追加された 著者 Ceasar Bautista,
これは私がPythonについて気に入らないことの一つです。あなたが文字列にint型のような型指定を強制するつもりならば、それをメソッドにするだけです。それを括弧で囲む必要があり時間がかかるというのは面倒です。
追加された 著者 Matt,
これは私がPythonを好きではない最も重要な理由です。何かを達成するために関数やメソッドを探す必要があるかどうかは決して分かりません。また、ベクターやデータフレームのような新しいデータ型で追加のライブラリを使用すると、さらに複雑になります。
追加された 著者 vonjd,
Meh、私はいつもそれをまったく恣意的と考えました。ダックタイピングは暗黙的な "インターフェース"を可能にしますが、 X.frob または X .__ frob __ と自立した frob <�コード>。
追加された 著者 Cat Plus Plus,
「PythonのZenは、そうでないことを除いて、何かを行う方法が1つだけあるべきだと述べています。
追加された 著者 gented,

6 答え

私の一般的なルールは次のとおりです。オブジェクトまたはオブジェクトで実行される操作ですか?

オブジェクトによって行われた場合は、メンバ操作でなければなりません。それが他のものにも当てはまるか、あるいはオブジェクトに何か他のものによって行われた場合、それは関数(または、おそらく他のもののメンバー)でなければなりません。

プログラミングを導入するとき、車のような実世界のオブジェクトに関してオブジェクトを記述するのは伝統的です(実装は間違っていますが)。あなたはアヒルに言及していますので、それを行こう。

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

「オブジェクトは本当のものです」という文脈では、オブジェクトが行うことのできるもののクラスメソッドを追加するのは「正しい」です。だから私はアヒルを殺したいと言っています。 。アヒルに殺す()?いいえ...私が知っている限り、動物は自殺しません。だから私がアヒルを殺したいなら、私はこれをするべきです:

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."

この類推から離れて、なぜメソッドとクラスを使うのでしょうか?データを格納し、将来的に再利用可能で拡張可能なようにコードを構築したいと思っているからです。これは、オブジェクト指向設計にとても愛されているカプセル化の概念に私たちをもたらします。

カプセル化プリンシパルは、実際には次のようになります。設計者は、実装やクラス内部に関するすべてを隠すべきです。ユーザーや他の開発者が必ずしもアクセスする必要はありません。クラスのインスタンスを処理するため、これは「このインスタンスでどのような操作が重要か」になります。操作がインスタンス固有でない場合は、メンバー関数であってはなりません。

TL;DR: what @Bryan said. If it operates on an instance and needs to access data which is internal to the class instance, it should be a member function.

63
追加された
要するに、非メンバ関数は不変オブジェクトで動作し、可変オブジェクトはメンバ関数を使用しますか? (これはあまりにも厳密な一般化ですか?不変型は状態を持たないので、これは特定の作業に対してのみです。)
追加された 著者 Ceasar Bautista,
食べる(自己)、ごみ(自己)、死ぬ(自己)。ハハハッハッハ
追加された 著者 Wapiti,
申し訳ありませんが、私はあなたがその点を逃したと思います。それはモデリングとセマンティクスに関するもので、より自然な音やより有用な音です。
追加された 著者 Karl Knechtel,
@Ceasarそれは本当ではない。不変オブジェクトには状態があります。そうでなければ、他のものから整数を区別することはありません。不変オブジェクトは状態を変更しません。一般的に、これにより、すべての州が一般に公表されることはそれほど問題になりません。そしてその設定では、関数を持つ不変のすべての公開オブジェクトを意味のある方法で操作するほうがはるかに簡単です。方法であるという「特権」はありません。
追加された 著者 Ben,
is キーワードはタイプを識別するのではなく、識別情報です。
追加された 著者 les,
@CeasarBautistaええ、ベンはポイントを持っています。コードデザインの3つの「主要な」学校があります.1)設計されていない、2)OOPと3)機能的です。機能的なスタイルでは、全く状態がありません。これは、ほとんどのPythonコードが設計されているのを見て、入力を受け取り、副作用の少ない出力を生成する方法です。 OOPのポイントは、すべてが状態であることです。クラスは状態のコンテナであり、したがって、「メンバ」関数は、呼び出されるたびにクラス内で定義された状態をロードする状態依存型および副作用ベースのコードです。 Pythonは機能的な傾向があるため、メンバーコード以外のものが優先されます。
追加された 著者 arrdem,
厳格なOOPの観点からは、それは公正だと思います。 Pythonには公開変数と非公開変数(名前が__で始まる変数)があり、Javaとは違って保証されたゼロアクセス保護を提供しているため、単に許可的な言語を議論しているため、絶対的なものはありません。しかし、Javaのようにあまり許容されない言語では、getFoo()のad setFoo()関数が標準であることに注意してください。したがって、不変性は絶対的なものではありません。クライアントコードでは、メンバーに代入することはできません。
追加された 著者 arrdem,

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

1)実装の詳細から呼び出しコードを分離する - 抽象化カプセル化を参照してください。

2)多形性を利用して、他のオブジェクトと置き換えたい場合。

3)継承を利用して、同様のオブジェクトにコードを再利用したい場合。

たとえば、組み込みの lenrepr 関数は多くの種類のオブジェクトに適用されます。

言われているように、選択は時々味の問題になる。典型的なコールで最も便利で読みやすいものを考えてみましょう。たとえば、次のようになります。(x.sin()** 2 + y.cos()** 2).sqrt()または sqrt(sin(x)** 2 + cos(y)** 2)

23
追加された

ここでは簡単な経験則があります。コードがオブジェクトの1つのインスタンスに作用する場合は、メソッドを使用します。さらに優れています:それを関数として書く魅力的な理由がない限り、メソッドを使用してください。

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

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

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

6
追加された
なぜあなたは関数の上にメソッドをデフォルトするのか説明できますか? (そして、スタックとキュー/ポップとプッシュメソッドの場合でもそのルールが意味をなさないかどうか説明できますか?)
追加された 著者 Ceasar Bautista,
STLにはそうするべきいくつかの理由があります。数学と共同のためには、クラスを持つことは明らかに無駄です(他の言語では静的関数のみを持つクラスです)。 len()のようないくつかの異なるコンテナで動作するものについては、私は個人的に関数がそれほど悪くないと思うかもしれないが、設計が意味をなさないと主張することもできる。 len()は常に整数を返さなければならないという別の規則を持っているだけです(しかし、バックコンペンセーションのすべての問題がありますが、
追加された 著者 Voo,
標準ライブラリがそれに違反しているのでルールが意味をなさないと言っていますか?私は結論が意味をなさないとは思わない。 1つは、大雑把なルールです。絶対的なルールではありません。さらに、標準ライブラリの大部分は、その経験則に従います 。 OPは、いくつかの簡単なガイドラインを探していました。私のアドバイスは、ちょうど出発する人にとってはまったく良いと思います。しかし、私はあなたの視点に感謝します。
追加された 著者 Bryan Oakley,
その経験則は意味をなさない。標準ライブラリ自体は、クラス対関数をいつ使用するかの指針になります。一般的なpythonオブジェクト(浮動小数点数やリスト)で動作するため、また random のような複雑な状態を維持する必要がないため、 heapq i>モジュールがあります。
追加された 著者 Raymond Hettinger,
-1はこの答えが完全に恣意的であるため、XはYより良いと実証しようとしています。
追加された 著者 gented,

私は通常、人のような物体を考える。

Attributes are the person's name, height, shoe size, etc.

Methods and functions are operations that the person can perform.

この操作を特定の人のみで行うことはできません(この1人の特定の人には何も変更しないで)、それは機能であり、そのような。

この人に踊る、本を書くなどの操作が人に作用する(例えば、食べる、歩く、など)、または ...)、それはメソッドでなければなりません。

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

6
追加された
これは当てはまりません。 are_married(person1、person2)の場合はどうなりますか?このクエリは非常に一般的なものであり、したがって関数でありメソッドではありません。
追加された 著者 Pithikos,
老人の高さを測定することができるので、そのロジックによって person.height ではなく height(person)でなければなりません。
追加された 著者 endolith,
確かに@Pithikosしかし、それは単なるObject(Person)に対して実行される属性やアクション以上のものではなく、2つのオブジェクト間の関係です。
追加された 著者 Thriveth,
@endolith一方、人が高さが増えて変化する子供の場合、関数ではなくメソッドがそれを処理するようにするのは明らかです。
追加された 著者 Thriveth,
@endolith確かに、高さは、それを検索するための派手な作業を行う必要がないため、属性としてより良いと言えます。数字を取得する関数を書くことは、ジャンプするための不要なフープのように思えます。
追加された 著者 Thriveth,

一般的に私はいくつかのことのための能力の論理的なセットを実装するためにクラスを使用するので、私のプログラムの残りの部分では、すべてのことを心配する必要はないことその実装を構成する少しの懸念である。

ものでできること」の中核的な抽象化の一部であるものは、通常、方法でなければなりません。通常、内部データの状態はプライベートとみなされ、「あなたがでできること」の論理的概念の一部ではないため、を変更することができるすべてが含まれます。

より高いレベルの操作、特に複数の物事が関わっている場合は、物事の一般的な抽象化から構築することができれば、/strong>(他のオブジェクトのメソッドでない限り)内部への特別なアクセスは必要ありません。これは、私のことの仕組み(インターフェースを変更せずに)の仕組みを完全に書き直すことに決めたときに、書き直すメソッドの小さなコアセットを持っているだけでなく、それらの方法の観点から書かれた関数はちょうどうまくいくでしょう。私は、クラスXと関係するすべての操作がクラスXのメソッドであると主張すると、過度に複雑なクラスにつながることがわかります。

それは私がしかし書いているコードに依存します。いくつかのプログラムでは、それらを相互作用がプログラムの振る舞いを引き起こすオブジェクトの集合としてモデル化します。ここで最も重要な機能は単一のオブジェクトに密接に結合されているため、ユーティリティ関数が散在するメソッドで実装されています。他のプログラムでは、最も重要なものはデータを操作する関数のセットであり、クラスは関数によって操作される自然な "ダックタイプ"を実装するためにのみ使用されます。

4
追加された

あなたは、「あいまいさに直面して、推測する誘惑を断る」と言うかもしれません。

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

私は、目標を達成するための複数の方法を持つことが唯一の良いことだと信じています。私は他のユーザーが既にしたように、言語の面でより "味が良く" /感じる直感的のいずれかを採用するよう、謙虚にあなたに伝えます。

0
追加された