web-dev-qa-db-ja.com

Pythonでインスタンス変数のデフォルト値を宣言するにはどうすればよいですか?

クラスメンバーに次のようなデフォルト値を与える必要があります。

class Foo:
    num = 1

またはこのような?

class Foo:
    def __init__(self):
        self.num = 1

この質問 では、どちらの場合でも、

bar = Foo()
bar.num += 1

明確に定義された操作です。

最初のメソッドはクラス変数を提供し、2番目のメソッドはクラス変数を提供しないことを理解しています。ただし、クラス変数を必要とせず、インスタンス変数のデフォルト値を設定するだけでよい場合、両方のメソッドは同等に優れていますか?または、それらの1つが他のPythonよりも「Python的な」ものですか?

私が気づいたことの1つは、Djangoチュートリアルでは、 モデルを宣言するために2番目のメソッドを使用します。 「標準的な」方法が何かを知りたい。

77
int3

Bpの答えを拡張して、不変の型が意味することを示したいと思いました。

まず、これは大丈夫です:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

ただし、これは不変(変更できない)型に対してのみ機能します。デフォルト値が変更可能である場合(置き換えることができることを意味する)、これは代わりに発生します。

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

abの両方が共有属性を持っていることに注意してください。多くの場合、これは望ましくありません。

これは、型が変更可能な場合にインスタンス変数のデフォルト値を定義するPythonの方法です。

>>> class TestC():
...     def __init__(self, attr=None):
...         if attr is None:
...             attr = []
...         self.attr = attr
...     
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

コードの最初のスニペットが機能する理由は、不変型では、Pythonが必要なときにいつでも新しいインスタンスを作成します。1に1を追加する必要がある場合、Pythonは、古い1は変更できないため、新しい2を作成します。

112
Xavier Ho

2つのスニペットは異なる処理を行うため、好みの問題ではなく、コンテキストの正しい動作の問題です。 Pythonドキュメント は違いを説明していますが、ここにいくつかの例を示します:

別紙A

class Foo:
  def __init__(self):
    self.num = 1

これにより、numがFooインスタンスにバインドされます。このフィールドへの変更は、他のインスタンスには伝播されません。

副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

図表B

class Bar:
  num = 1

これはnumをBarclassにバインドします。変更が反映されます!

>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3

実際の答え

クラス変数を必要とせず、インスタンス変数にデフォルト値を設定するだけでよい場合、両方のメソッドは同等に優れていますか?または、それらの1つが他のPythonよりも「Pythonic」ですか?

展示Bのコードは、この点で明らかに間違っています。なぜクラス属性(インスタンス作成時のデフォルト値)を単一のインスタンスにバインドするのでしょうか?

展示Aのコードは大丈夫です。

ただし、コンストラクターでインスタンス変数のデフォルトを指定する場合は、次のようにします。

class Foo:
  def __init__(num = None):
    self.num = num if num is not None else 1

...あるいは:

class Foo:
  DEFAULT_NUM = 1
  def __init__(num = None):
    self.num = num if num is not None else DEFAULT_NUM

...または偶数:(ただし、不変の型を処理している場合にのみ、好ましい!)

class Foo:
  def __init__(num = 1):
    self.num = num

この方法でできること:

foo1 = Foo(4)
foo2 = Foo() #use default
49
badp

クラスメンバーを使用してデフォルト値を与えることは、不変値でのみ行うように注意している限り、非常にうまく機能します。リストや辞書を使ってそれをやろうとすると、かなり致命的です。また、デフォルト値がNoneである限り、インスタンス属性がクラスへの参照である場合にも機能します。

この手法は、Zopeの上で実行されるフレームワークであるrepozeで非常にうまく使用されるのを見てきました。ここでの利点は、クラスをデータベースに永続化するときにデフォルト以外の属性のみを保存する必要があるだけでなく、スキーマに新しいフィールドを追加する必要がある場合にも、既存のすべてのオブジェクトにデフォルトの新しいフィールドが表示されることです保存されたデータを実際に変更する必要のない値。

私はそれがより一般的なコーディングでもうまくいくと思うが、それはスタイルのことだ。最も幸せなものを使用してください。

4
Duncan

インスタンス変数のデフォルト値にクラスメンバーを使用するのは良い考えではなく、この考えがまったく言及されたのは初めてです。あなたの例ではうまくいきますが、多くの場合失敗するかもしれません。たとえば、値が変更可能な場合、変更されていないインスタンスで値を変更すると、デフォルトが変更されます:

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
3
Max Shawabkeh

クラス変数をNoneとして宣言して、伝播を防ぐこともできます。これは、適切に定義されたクラスが必要で、AttributeErrorsを防ぎたい場合に便利です。例えば:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

また、デフォルトが必要な場合:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

もちろん、__init__のデフォルトとして可変型と不変型を使用することに関する他の回答の情報に従ってください。

1
Realistic