12

重複の可能性:
Pythonの「自己」の説明

私はPythonを学んでおり、このクラスのメソッドからのアクセスクラス変数に関して、実用的というより理論的な質問があります。

たとえば、次のようになります。

class ExampleClass:
    x = 123
    def example_method(self):
        print(self.x)

self.xなぜだけでなく、正確に書く必要があるのxですか?xクラスの名前空間に属し、それを使用するメソッドもそれに属します。私は何が欠けていますか?そのようなスタイルの背後にある理論的根拠は何ですか?

C ++では、次のように記述できます。

class ExampleClass {
public:
    int x;
    void example_method()
    {
        x = 123;
        cout << x;
    };
};

そしてそれはうまくいくでしょう!

4

4 に答える 4

28

Pythonの歴史から:ユーザー定義クラスのサポートの追加

代わりに、インスタンス変数への暗黙の参照のアイデアをあきらめることにしました。C ++のような言語では、this-> fooを記述して、インスタンス変数fooを明示的に参照できます(別のローカル変数fooがある場合)。したがって、このような明示的な参照をインスタンス変数を参照する唯一の方法にすることにしました。さらに、現在のオブジェクト( "this")を特別なキーワードにするのではなく、単に "this"(またはそれに相当するもの)をメソッドの最初の名前付き引数にすることにしました。インスタンス変数は、常にその引数の属性として参照されます。

明示的な参照を使用すると、メソッド定義に特別な構文を設定する必要も、変数ルックアップに関する複雑なセマンティクスについて心配する必要もありません。代わりに、最初の引数がインスタンスに対応する関数を定義するだけです。このインスタンスは、慣例により「自己」と呼ばれます。例えば:

def spam(self,y):
    print self.x, y

このアプローチは、インポートと例外処理の構文をすでに提供しているModula-3で見たものに似ています。Modula-3にはクラスがありませんが、近くで定義された関数にデフォルトで初期化される完全に型指定された関数ポインターメンバーを含むレコード型を作成でき、xがそのようなレコード変数であり、mがそのレコードの関数ポインタメンバーで、関数fに初期化されてから、xm(args)を呼び出すことは、f(x、args)を呼び出すことと同じです。これは、オブジェクトとメソッドの一般的な実装と一致し、インスタンス変数を最初の引数の属性と同一視することを可能にします。

したがって、BDFL自身が述べたように、彼が暗黙の自己よりも明示的な自己を使用することを決定した唯一の本当の理由は、次のとおりです。

  • 明示的です
  • ルックアップは実行時に(他の言語のようにコンパイル時にではなく)実行する必要があり、暗黙の自己を持つとルックアップの複雑さ(したがってコスト)が増加する可能性があるため、実装が簡単です。

編集:PythonFAQにも回答があります

于 2012-11-30T19:58:50.370 に答える
7

Pythonでは、モジュールとクラスのスコープ処理に関連しているようです。

COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue
于 2012-11-30T19:51:33.893 に答える
6

この機能に関する古代の回答で私が行った内容は次のとおりです。


発生した問題は、次の理由によるものです。

ブロックは、ユニットとして実行されるPythonプログラムテキストの一部です。ブロックは次のとおりです。モジュール、関数本体、およびクラス定義。

(...)

スコープは、ブロック内の名前の可視性を定義します。

(...)

クラスブロックで定義されている名前の範囲は、クラスブロックに限定されています。メソッドのコードブロックには拡張されません。関数スコープを使用して実装されるため、ジェネレーター式が含まれます。これは、以下が失敗することを意味します。

クラスA:

   a = 42  

   b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

上記の意味:関数本体はコードブロックであり、メソッドは関数であり、クラス定義に存在する関数本体から定義された名前は関数本体に拡張されません。


私がこれを読んでいたとき、それは私には奇妙に見えました、しかしそれはPythonが作られている方法です:

クラスブロックで定義されている名前の範囲は、クラスブロックに限定されています。メソッドのコードブロックには拡張されません

これは、これを示す公式ドキュメントです。

編集

heltonbikerは興味深いコードを書きました:

COLOR = 'blue'

class TellColor(object):
    COLOR = 'red'

    def tell(self):
        print self.COLOR   # references class variable
        print COLOR        # references module variable

a = TellColor()
a.tell()

> red
> blue

print COLORメソッド内に記述された命令がtell()、クラス外で定義されたグローバルオブジェクトCOLORの値の出力をどのように引き起こすのか不思議に思いました。
私は公式文書のこの部分で答えを見つけました:

メソッドは、通常の関数と同じ方法でグローバル名を参照できます。メソッドに関連付けられたグローバルスコープは、その定義を含むモジュールです。(クラスがグローバルスコープとして使用されることはありません。)メソッドでグローバルデータを使用する正当な理由に遭遇することはめったにありませんが、グローバルスコープの正当な使用法はたくさんあります。たとえば、グローバルスコープにインポートされた関数とモジュールは次のことができます。メソッド、およびメソッドで定義された関数とクラスによって使用されます。通常、メソッドを含むクラス自体は、このグローバルスコープで定義されます(...)

http://docs.python.org/2/tutorial/classes.html#method-objects

COLORはインスタンス属性ではないため(つまり、識別子'COLOR'はインスタンスの名前空間に属していないため)、インタプリタが実行する必要がある場合、インタプリタはインスタンスのクラスの名前空間に移動しprint self.COLORます。識別子「COLOR」を検索して見つけると、TellColor.COLORの値が出力されます。

インタプリタが実行する必要がある場合print COLOR、この命令には属性アクセスが記述されていないため、グローバル名前空間で識別子「COLOR」を検索します。これは公式ドキュメントにモジュールの名前空間であると記載されています。

于 2012-11-30T19:59:02.680 に答える
3

オブジェクト(およびそのクラス、およびそのクラスの祖先)に付けられている属性名は、コンパイル時に決定できません。したがって、属性ルックアップを明示的にするか、次のようにします。

  • (メソッド内の)ローカル変数を根絶し、常にインスタンス変数を使用します。これは、(少なくともメソッドでは)すべての利点を備えたローカル変数を本質的に削除するため、効果がありません。
  • ベースが属性を参照するか、実行時にローカルを参照するかを決定します(新しい属性がない場合にxいつ追加するかを決定するためのいくつかの追加ルールがあります)。これにより、名前がどの名前であるかがわからないため、コードが読みにくくなり、基本的にすべてのメソッドのすべてのローカル変数がパブリックインターフェイスの一部になります(その名前の属性をアタッチするとメソッドの動作が変わるため)。x = ...self.x

どちらにも、メソッドに特別なケーシングが必要になるという追加の欠点があります。現在、「メソッド」は、クラス属性を介してアクセスできる通常の関数です。これは、さまざまな使用例に非常に役立ちます。

于 2012-11-30T19:49:55.827 に答える