web-dev-qa-db-ja.com

Pythonオプションのパラメータ

みんな、私は最近pythonを始めたばかりで、オプションのパラメータと混同されています。たとえば、次のようなプログラムがあるとしましょう。

class B:
   pass

class A:
    def __init__(self, builds = B()):
        self.builds = builds

Aを2回作成すると

b = A()
c = A()

ビルドを印刷します

print b.builds
print c.builds

まったく同じオブジェクトを使用していることがわかりました。

<__main__.B instance at 0x68ee0>
<__main__.B instance at 0x68ee0>

しかし、それは私が望んでいることではありません。bがビルドの内部状態を変更すると、cオブジェクトの状態も変更されるからです。

このオプションのパラメーター構文を使用して、このオプションのパラメーターを毎回再作成することは可能ですか?

24
user192048

デフォルト値を効果的に使用するには、デフォルト値がどのように機能するかを理解する必要があります。

関数はオブジェクトです。そのため、それらには属性があります。したがって、この関数を作成すると、次のようになります。

>>> def f(x, y=[]):
        y.append(x)
        return y

オブジェクトを作成しました。その属性は次のとおりです。

>>> dir(f)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',   
'__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__',    
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 
'func_name']

それらの1つはfunc_defaultsです。それは有望に聞こえます、そこには何がありますか?

>>> f.func_defaults
([],)

これは、関数のデフォルト値を含むタプルです。デフォルト値がオブジェクトの場合、タプルにはそのオブジェクトのインスタンスが含まれます。

これは、fがリストにアイテムを追加し、リストが提供されていない場合はそのアイテムのみを含むリストを返すと考えている場合、かなり直感に反する動作につながります。

>>> f(1)
[1]
>>> f(2)
[1, 2]

ただし、デフォルト値が関数の属性の1つに格納されているオブジェクトインスタンスであることがわかっている場合は、直感に反することはほとんどありません。

>>> x = f(3)
>>> y = f(4)
>>> x == y
True
>>> x
[1, 2, 3, 4]
>>> x.append(5)
>>> f(6)
[1, 2, 3, 4, 5, 6]

これを知っていると、関数のパラメータのデフォルト値を新しいリスト(または新しいオブジェクト)にしたい場合、オブジェクトのインスタンスをfunc_defaultsに単純に隠しておくことはできないことは明らかです。関数が呼び出されるたびに、新しい関数を作成する必要があります。

>>>def g(x, y=None):
       if y==None:
           y = []
       y.append(x)
       return y
47
Robert Rossney

次のことを行う必要があります。

class A:
    def __init__(self, builds=None):
        if builds is None:
            builds = B()
        self.builds = builds

これは非常に広範囲にわたるエラーであり、デフォルトの引数として可変パラメーターを使用します。おそらくSOにはたくさんの重複があります。

15
SilentGhost

はい;デフォルトのパラメーターは、関数が定義されたときにのみ評価されます。

考えられる解決策の1つは、パラメーターをインスタンスではなくclassにすることです。

def foo(blah, klass = B):
    b = klass()
    # etc
6