16

私はPythonには比較的新しいので、C++やJavaのバックグラウンドから習得した習慣と言語の機能を調和させるのに苦労しています。非会員vs Pythonのメンバー関数

私がいる最新号は、カプセル化して最高のマイヤーの「効果的なC++」の項目23でまとめ、具体的なアイデアを行うことがあります。

Prefer non-member non-friend functions to member functions。しばらくfriend機構の欠如を無視

を非メンバ関数も、Pythonのにメンバ関数に好ましいと考えていますか?

義務、愚か例:v = Vector(10, 20)考える

class Vector(object): 
    def __init__(self, dX, dY): 
     self.dX = dX 
     self.dY = dY 

    def __str__(self): 
     return "->(" + str(self.dX) + ", " + str(self.dY) + ")" 

    def scale(self, scalar): 
     self.dX *= scalar 
     self.dY *= scalar 

def scale(vector, scalar): 
    vector.dX *= scalar 
    vector.dY *= scalar 

、我々は今、どちらかのベクトルの大きさを倍増させるv.scale(2)またはscale(v, 2)を呼び出すことができます。優れている、と理由 - があれば -

私たちは、この場合にプロパティを使用しているという事実は、二つの選択肢のを考えると?

+2

これは単にPythonでは当てはまりません。引数は実際にはPythonで座っていないので、クラスを簡単に変更できます。 Pythonは読みやすさにも焦点を当てています。私は '' v.scale(2) ''が '' scale(v、2) ''よりもはるかに明確であると感じています。標準ライブラリを見ると、最も一般的な関数を除くすべての関数は、組み込み関数ではなくメンバとして保持されます。 –

答えて

15

興味深い質問です。

あなたは、Javaプログラマーからのほとんどの質問とは別の場所から始めています。ほとんどの場合、クラスが必要であると思われる傾向があります。一般に、Pythonでは、特にデータのカプセル化をしていない限り、クラスを持つことに意味がありません。

もちろん、あなたの例では、実際にはそうしているので、クラスの使用は正当です。個人的には、以降では、にクラスがあるとします。メンバ関数が最適な方法です。具体的にはその特定のベクトルインスタンスに対して操作を行っているので、関数がa Vector上のメソッド。

これは、必ずしも継承されない複数のクラスで動作させる必要がある場合、スタンドアロン関数(実際には「member」または「non-member」という単語は使用しません)にしたい場合があります互いにまたは共通の基盤からのものである。ダックタイピングのおかげで、これを行うのはかなり一般的なことです:あなたの関数が特定の属性やメソッドのセットを持つオブジェクトを期待していることを指定し、それらを使って何かを行います。

+3

C++との違いは、Pythonには 'public'や' private'という概念がないという点です。 '_underscore'構文は、何かがプライベートであるが、*強制されていない*ヒント*を提供する従来の方法です。 –

2

独自の例を見てください。非メンバ関数は、Vectorクラスのデータメンバーにアクセスする必要があります。これはカプセル化のための勝利ではありません。これは、渡されたオブジェクトのデータメンバを変更するときに特にそうです。この場合、スケーリングされたベクトルを返し、元のまま変更しない方がよい場合があります。

さらに、非メンバ関数を使用してクラス多形性の利点を理解することはできません。例えば、この場合、2つの成分のベクトルにしか対処できない。ベクトル乗算機能を使用した方が良いか、またはコンポーネントを反復処理するメソッドを使用する方が良いでしょう。要約すると

  1. 使用メンバ関数は、クラスのオブジェクトを操作するために、あなたがコントロールします。
  2. 非メンバ関数を使用して純粋に汎用的な演算を実行します。これらの演算は、それ自体が多相であるメソッドと演算子によって実装されます。
  3. おそらく、メソッドにオブジェクトの変異を保存する方が良いでしょう。
+0

アクセスされるメンバー(xおよびyコンポーネント)は、オブジェクトが有用であるためには、いずれにせよパブリックでなければなりません。次元が異なるベクトルを考えると、多形性についてのポイントがあります。 – delnan

+1

@delnan私はスタンドアロンの機能をカプセル化違反と呼んでいるわけではありません。むしろ、カプセル化をどのようにも改善しないことは明らかです。私はその一般性の欠如を認めていません - それは2つの要素のベクトルにしか対処できません。私はまた、ベクトルのデータを変更するという事実を否定します。 – Marcin

+1

@Marcin - この例のオブジェクトの名前は無関係です。実際、私の防衛では、asinineとしてラベル付けされていました。この質問は、メンバー/非メンバー関数の相対的メリットについてのコメントと、任意のサンプルをより一般的なものにする方法に関する提案ではない*コメント*を求めています。 – JimmidyJoo

4

フリー機能を使用すると、最初のパラメータにダックタイピングを柔軟に使用できます。

メンバー関数を使用すると、機能をクラスに関連付けることができます。

それに応じて選択してください。一般的に、関数は等しく作成されるため、クラスのインタフェースについてはすべて同じ仮定が必要です。無料の機能scaleを公開すると、.dX.dYが公開インタフェースの一部であるVectorになることを効果的に宣伝しています。それはおそらくあなたが望むものではありません。これは、.dX.dYを持つ他のオブジェクトと同じ関数を再利用することと引き換えに行います。それはおそらくあなたにとって貴重ではないでしょう。この場合、私は確かにメンバー関数を好むでしょう。

無料の機能を好む良い例として、sortedは無料の関数であり、listのメンバー関数ではありません。概念的には、結果として得られるリストを作成できるはずです任意の反復可能な配列のソートから。

3

これは設計思想であるとし、すべてのOOPパラダイムプログラミング言語に拡張する必要がありますすることができ

メンバ関数に非会員非フレンド関数を好みます。

クラスのメンバーへのプライベート/保護されたアクセスを必要とせずに行うことができる場合、デザインにはその機能を組み込む理由がなく、クラスのメンバー。これを他の方法で考えるには、クラスを設計するときに、すべてのプロパティを列挙した後、クラスを作成するのに十分な最小限のビヘイビアセットを決定する必要があります。使用可能なパブリックメソッド/メンバー関数のいずれかを使用して記述できるメンバー関数はすべて公開する必要があります。

あなたが注意している場合、これはある程度のPython

に適用いくらです。 Pythonは他のOOP言語(Java/C++など)と比較して弱いカプセル化をサポートしています。特にプライベートメンバーがないためです。 (変数名の前に '_'を接頭辞として付けることで、プログラマが簡単に書くことができるPrivate変数と呼ばれるものがあります。これは、名前のマングリング機能によってプライベートクラスになります)。だから、スコット・マイヤーの言葉を文字通り採用すれば、クラスからアクセスすべきものと外部からのものとの間のような薄いものがあるとみなしている。関数がClassまたはNotの不可欠な部分であるかどうかを決めるには、デザイナー/プログラマーに任せておくのが最善です。我々は簡単にscaleは、ベクターのメンバーごとの乗算に依存しているとして、私は方法として乗算を実施し、より一般的であることをscaleを定義する検討する、"Unless your function required to access any of the properties of the class you can make it a non-member function".

1

を採用することができます一つの設計原理:

class Vector(object): 
    def __init__(self, dX, dY): 
     self._dX = dX 
     self._dY = dY 

    def __str__(self): 
     return "->(" + str(self._dX) + ", " + str(self._dY) + ")" 

    def __imul__(self, other): 
     if other is Vector: 
      self._dX *= other._dX 
      self._dY *= other._dY 
     else: 
      self._dX *= other 
      self._dY *= other 

     return self 

def scale(vector, scalar): 
    vector *= scalar 

このように、カプセル化が維持されている間、クラスインタフェースは豊富で合理化されています。

+0

これは私の具体的な例にとっては間違いなく面白いアプローチだと思います。 – JimmidyJoo

関連する問題