5

属性の辞書を持つオブジェクトがいくつかありますobj.attrs。これらのオブジェクトのコンストラクターは**kwargs、便宜上、 dict および/または を受け入れます。

次のようになります。

class Thing:
    def __init__(self, attrs={}, **kwargs):
        for arg in kwargs:
            attrs[arg] = kwargs[arg]
        self.attrs = attrs

そのようなことThing({'color':'red'})は と同じThing(color='red')です。

私の問題は、コンストラクターが渡された最後の値をどういうわけか覚えていることです。attrs

例えば:

>>> thing1 = Thing(color='red')
>>> thing2 = Thing()
>>> thing2.attrs
{'color': 'red'}

...しかしthing2.attrs、空の辞書である必要があります!{}

これは、両方 **kwargs のような引数を使用しても問題ないのではないかと思いattrs={}ました。

何か案は?

4

5 に答える 5

11

デフォルト引数を使用する際の問題は、それらのインスタンスが実際に 1 つしか存在しないことです。initメソッド定義で言うとattrs={}、その単一のデフォルトの {} インスタンスが、そのメソッドへのすべての呼び出しのデフォルトになります (毎回新しいデフォルトの空の dict を作成するのではなく、同じものを使用します)。

問題は、存在するのが 1 つだけattrsで、 Thing のすべてのインスタンスについて、インスタンスのすべてのインスタンスのself.attrs = attrsメンバーself.attrs変数が の単一の共有デフォルト インスタンスを指している場合ですattrs

他の質問は、これは完全に冗長ではありませんか? を使用**kwargsして、キーワード/値の引数または辞書を渡すことができます。これを定義した場合:

class Thing:
    def __init__(self, **kwargs):
        for arg in kwargs:
            self.attrs[arg] = kwargs[arg]

これらの戦略はすべて引き続き機能します。

thing1 = Thing(color='red')

thing2 = Thing(**{'color':'red'})

my_dict = {'color' : 'red'}
thing3 = Thing(**my_dict)

そのため、単に Thing をそのように定義して使用すると、問題を完全に回避できます。

于 2011-07-22T18:15:37.450 に答える
2

辞書が毎回作成されるように署名を変更するのはどうですか

class Thing:
    def __init__(self, attrs=None, **kwargs):
        self.attrs = attrs or {}
        self.attrs.update(kwargs)
于 2011-07-22T18:20:23.417 に答える
1

attrs辞書への参照です。新しいオブジェクトを作成するself.attrsと、そのディクショナリがポイントされます。から値を割り当てると、kwargsこのディクショナリに移動します。

ここで、2 番目のインスタンスを作成すると、それself.attrsも同じディクショナリを指します。したがって、その辞書にあるデータは何でも取得します。

これに関する素晴らしい議論については、Python の「Least Astonishment: The Mutable Default Argument here on stackoverflow」を参照してください。effbotの Python のデフォルト パラメータ値も参照してください。

于 2011-07-22T18:16:40.483 に答える
1

コードを次のように変更します。

class Thing:
    def __init__(self, attrs=None, **kwargs):
        attrs = {} if attrs is None else attrs
        for arg in kwargs:
            attrs[arg] = kwargs[arg]
        self.attrs = attrs

他の人が指摘したように、デフォルト引数の値は、関数が呼び出されるたびにではなく、定義時に一度評価されます。変更可能なコンテナーを使用すると、各呼び出しが既定値として同じコンテナーを使用するため、コンテナーへの各追加は後続のすべての呼び出しで認識されます。

初期値を提供する方法としてのみ attrs を使用し、辞書を共有するつもりはまったくなかったかもしれません。その場合、これを使用します。

class Thing:
    def __init__(self, attrs=None, **kwargs):
        self.attrs = {}
        if attrs:
            self.attrs.update(attrs)
        for arg in kwargs:
            self.attrs[arg] = kwargs[arg]
于 2011-07-22T18:18:27.503 に答える
1

それだけの価値があります-attrs単に変更しないことで、「可変共有オブジェクトです」という問題を回避できます。kwargsにダンプする代わりに、attrs両方を新しい dict にダンプします。その場合、default-argument-object は常に になります{}

class Thing:
    def __init__(self, attrs = {}, **kwargs):
        self.attrs = {}
        # Don't write the loop yourself!
        self.attrs.update(attrs)
        self.attrs.update(kwargs)

これについて言及しているのは、誰もが「None をデフォルトの引数として使用し、それをチェックする」という慣用句を急いで説明しているからです。sgusc は正しい考えを持っています: Python の**kwargs. :)

于 2011-07-22T19:22:23.460 に答える