2013-03-03 9 views
19

私は、このクラスから取られたデフォルト値の引数を持つクラスの中にメソッドを構築したいと思います。一般的に私はいくつかのデータをフィルタリングします。私のクラスの中には、通常はデータのベクトルを渡すメソッドがあります。時々私はベクトルを持っていないので、私はシミュレートされたデータを取る。私が特定のベクトルを渡さないたびに、私はデフォルトでシミュレーションされたデータを取りたいと思います。私はメソッド定義の中で私がa=self.vectorと言っているのは簡単な構造でなければならないと思いました。しかし何らかの理由で私はエラーNameError: name 'self' is not definedを持っています。単純化工事は次のとおりです。クラスメソッドの引数にデフォルト値としてクラス変数を代入

class baseClass(object): # This class takes an initial data or simulation 
    def __init__(self): 
     self.x = 1 
     self.y = 2 

class extendedClass(baseClass): # This class does some filtering 
    def __init__(self): 
     baseClass.__init__(self) 
     self.z = 5 
    def doSomething(self, a=self.z): 
     self.z = 3 
     self.b = a 

if __name__ == '__main__': 
    a = extendedClass() 
    print a.__dict__ 
    a.doSomething() 
    print a.__dict__ 

私が期待される出力は次のようになります。

{'y': 2, 'x': 1, 'z': 5} 
{'y': 2, 'x': 1, 'z': 3, 'b': 5} 

def doSomething(self, a=z):は明らかにそれが今まで動作しませんように私は、デフォルトの割り当てを試してみました。私が理解する限り、self.zはこのスコープ内に表示され、デフォルト値として持つべきではありません。なぜ私はこのエラーがあり、それを行う方法がわからない。これはおそらく簡単な質問ですが、私はそれを理解しようとするか、またはいつかは不足していない解決策を見つけようとします。 similar質問は他の言語でのみ見つかりました。

答えて

25

あなたの理解は間違っています。 selfはそれ自体がその関数定義のパラメータであるため、その時点でスコープに入ることはできません。関数内のスコープ内にあるだけです。

答えがNoneに引数をデフォルトして、メソッド内でそれをチェックすることです:定義が実行されたとき

def doSomething(self, a=None): 
    if a is None: 
     a = self.z 
    self.z = 3 
    self.b = a 
+0

引数が偽値をとることができない場合は、 'if a:a = self.z'または' a = a or self.z'と書いてください。 – danijar

+0

私はあなたが 'a not:a = self.z'を意味すると思います。 – BBischof

+0

これは標準的なpythonicパターンですか?つまり、オプションのメソッドプロパティからのフォールバックとして使用されるデフォルトのクラスプロパティですか?あるいは、オプションの 'z = None'プロパティを含むように' extendedClass' __init__コンストラクタを変更し、 'None'をメソッド内部のデフォルトに設定し、それをメソッドパラメータとして持たないようにする方が良いでしょうか?ハードコーディングされたクラスプロパティを持つことは本当に定数で、複数のメソッドで必要な場合に便利です。そうでなければ 'z 'をメソッドのスコープに制限するだけでなく、クラスやオブジェクトがないすべてのプロパティですか? – Davos

6

ここでは、簡単なサンプルモジュールのコードの逆アセンブリについて説明します。コードオブジェクトは、バイトコード、それが使用する定数および名前、ローカル変数の数、必要なスタックサイズなどに関するメタデータの読み取り専用コンテナです。すべてのコードオブジェクトは定数としてコンパイルされます。これらはコンパイル時に作成されます。しかし、オブジェクトclass Afunction testは、実行時にインスタンス化されます(モジュールがインポートされるときなど)。クラスを作るために

BUILD_CLASSは、クラスの名前空間の属性が含まれている拠点tuple(object,)、名前'A'を取り、dict。これは、type(name, bases, dict)を呼び出すことによって手動で型をインスタンス化するのと同じです。 dictを作成するには、コードオブジェクトAから関数を作成して呼び出す。最後に、クラスオブジェクトはSTORE_NAMEを介してモジュール名前空間に格納されます。

コードオブジェクトAでは、の引数としてself.zがスタックにロードされます。バイトコードop LOAD_NAMEは、現在のローカル(つまり、定義されているクラスの名前空間)内のself、モジュールのグローバル、および組み込みコードを検索します。 selfがglobalまたはbuiltinsスコープで定義されていない場合、これは明らかに失敗します。明らかにローカルスコープでは定義されていません。それが成功しなかった場合は

、しかし、機能はその__defaults__属性として(self.z,)で作成し、ローカル名testに格納されます。

>>> code = compile(''' 
... class A(object): 
... def test(self, a=self.z): pass 
... ''', '<input>', 'exec') 

>>> dis.dis(code) 
    2   0 LOAD_CONST    0 ('A') 
       3 LOAD_NAME    0 (object) 
       6 BUILD_TUPLE    1 
       9 LOAD_CONST    1 (<code object A ...>) 
      12 MAKE_FUNCTION   0 
      15 CALL_FUNCTION   0 
      18 BUILD_CLASS   
      19 STORE_NAME    1 (A) 
      22 LOAD_CONST    2 (None) 
      25 RETURN_VALUE 

>>> dis.dis(code.co_consts[1]) # code object A 
    2   0 LOAD_NAME    0 (__name__) 
       3 STORE_NAME    1 (__module__) 

    3   6 LOAD_NAME    2 (self) 
       9 LOAD_ATTR    3 (z) 
      12 LOAD_CONST    0 (<code object test ...>) 
      15 MAKE_FUNCTION   1 
      18 STORE_NAME    4 (test) 
      21 LOAD_LOCALS   
      22 RETURN_VALUE  

@uselpa:(2.xのために書き換えられた)あなたペーストビン例:

>>> code = compile(''' 
... default = 1 
... class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... Cl() 
... default = 2 
... Cl() 
... ''', '<input>', 'exec') 
>>> dis.dis(code) 
    2   0 LOAD_CONST    0 (1) 
       3 STORE_NAME    0 (default) 

    3   6 LOAD_CONST    1 ('Cl') 
       9 LOAD_NAME    1 (object) 
      12 BUILD_TUPLE    1 
      15 LOAD_CONST    2 (<code object Cl ...>) 
      18 MAKE_FUNCTION   0 
      21 CALL_FUNCTION   0 
      24 BUILD_CLASS   
      25 STORE_NAME    2 (Cl) 

    6   28 LOAD_NAME    2 (Cl) 
      31 CALL_FUNCTION   0 
      34 POP_TOP    

    7   35 LOAD_CONST    3 (2) 
      38 STORE_NAME    0 (default) 

    8   41 LOAD_NAME    2 (Cl) 
      44 CALL_FUNCTION   0 
      47 POP_TOP    
      48 LOAD_CONST    4 (None) 
      51 RETURN_VALUE   

あなたは、クラスオブジェクトCl(および関数オブジェクト__init__)を見ることができるようにだけインスタンス化されローカル名'Cl'に一度格納されます。モジュールは実行時に順番に実行されるため、defaultという名前を再バインドすると、デフォルト値の__init__には影響しません。新しい__defaults__タプルで関数を作成する。これは、__init__.__code__からすでにコンパイルされたコードオブジェクトを再利用する

>>> default = 1 
>>> class Cl(object): 
...  def __init__(self, a=default): 
...   print a 
... 

>>> from types import FunctionType 
>>> default = 2 
>>> Cl.__init__ = FunctionType(
... Cl.__init__.__code__, globals(), '__init__', (default,), None) 
>>> c = Cl() 
2 

:あなたは、動的にあらかじめコンパイルされたコードと新しいデフォルト値を使用して新しい機能をインスタンス化でき

>>> Cl.__init__.__defaults__ 
(2,) 
+1

私は立ち往生しています。インスタンス化時にクラスとメソッドが作成された場合、なぜ私の例では明らかにhttp://pastebin.com/UJfMpnqWにはありませんか? – uselpa

+0

@uselpa:「コンパイル時間」と「実行時間」の違いを明確にするために、あなたのサンプルを追加しました。モジュールは構文エラーなしで正常にコンパイルできますが、インポート(実行)時にはランタイムエラーが発生します。 – eryksun

+0

私はそれを得ていると思う。あなたのご忍耐をありがとうございます! – uselpa

関連する問題