web-dev-qa-db-ja.com

クラスメソッドの引数にデフォルト値としてクラス変数を割り当てる

このクラスから取得したデフォルト値の引数を使用して、クラス内にメソッドを構築したいと思います。一般的に、いくつかのデータでフィルタリングを行います。クラス内には、通常、データのベクトルを渡すメソッドがあります。時々、ベクターがなく、シミュレートされたデータを取得します。特定のベクトルを渡さないたびに、デフォルトでシミュレーションデータを取得したいと思います。メソッド定義内で_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 __== '__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 他の言語のみの質問を見つけました。

42
tomasz74

あなたの理解は間違っています。 selfは、それ自体がその関数定義のパラメーターであるため、その時点でスコープ内に存在する方法はありません。関数自体の範囲内にのみあります。

答えは、単に引数をNoneにデフォルト設定し、メソッド内でそれを確認することです。

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a
50
Daniel Roseman

簡単なサンプルモジュールのコードの逆アセンブリを次に示します。コードオブジェクトは、バイトコード、使用する定数と名前、ローカル変数の数、必要なスタックサイズなどに関するメタデータの読み取り専用コンテナーです。すべてのコードオブジェクトは定数としてコンパイルされることに注意してください。これらはコンパイル時に作成されます。ただし、オブジェクト_class A_および_function test_は、実行時にインスタンス化されます(たとえば、モジュールがインポートされるとき)。

クラスを作成するために、_BUILD_CLASS_は名前_'A'_、ベースTuple _(object,)_、およびクラス名前空間の属性を含むdictを取ります。これは、type(name, bases, dict)を呼び出して手動で型をインスタンス化するようなものです。 dictを作成するには、コードオブジェクトAから関数を作成して呼び出します。最後に、クラスオブジェクトは_STORE_NAME_を介してモジュール名前空間に保存されます。

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

ただし、成功した場合、関数は_(self.z,)_属性として___defaults___を使用して作成され、ローカル名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: Pastebinの例(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___のデフォルト値には影響しません。

以前にコンパイルされたコードと新しいデフォルト値を使用して、新しい関数を動的にインスタンス化できます。

_>>> 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
_

これは、___init__.__code___から既にコンパイルされたコードオブジェクトを再利用して、新しい___defaults___タプルで関数を作成します。

_>>> Cl.__init__.__defaults__
(2,)
_
7
Eryk Sun

デフォルト引数は、定義が実行されるときに一度だけ評価されます。代わりに、これを行います:

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

http://docs.python.org/release/3.3.0/tutorial/controlflow.html#more-on-defining-functions もご覧ください。

5
uselpa

これにより、self.z if aNone/False/empty_value

def doSomething(self, a=None):
        self.z = 3
        self.b = (a or self.z)
1
Alex