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


90

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

class Foo:
    num = 1

またはこのように?

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

、この質問私は、両方のケースでこれを発見しました

bar = Foo()
bar.num += 1

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

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

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

回答:


132

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に追加する必要がある場合、古い1は変更できないため、Pythonは新しい2を作成します。その理由は主にハッシュのためだと私は信じています。


2
私は最近、可変属性のデフォルト値を実装するこのはるかに簡単な方法に出くわしました。メソッドself.attr = attr or []内に書き込むだけ__init__です。それは同じ結果を達成し(私は思う)、それでも明確で読みやすいです。
ビル

4
申し訳ありませんが。私は上記のコメントの提案についてさらに調査を行いましたが、明らかに同じではなく、推奨されていません
ビル

TestCクラスに空の括弧があるのはなぜですか?それはPython2のレガシーですか?
ChrisG

55

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

展示物A

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

これはバインドします numFooインスタンスにます。このフィールドへの変更は、他のインスタンスには反映されません。

したがって:

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

展示物B

class Bar:
  num = 1

これはnumBarクラスにバインドしますます。変更が伝播されます!

>>> 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つは他よりも「pythonic」ですか?

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

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

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

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

...あるいは:

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

...または:(望ましいですが、不変の型を扱っている場合に限ります!)

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

このようにして、次のことができます。

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

1
この例で使用されているinitメソッドは、初期化@badpのunderscore ___ init__underscoreとは異なります
Eliethesaiyan 2015

最初の例はすべきではありませんdef __init__(self, num = None)
PyWalker27 9719年

6

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

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

より一般的なコーディングでもうまく機能することがわかりましたが、それはスタイルの問題です。あなたが最も幸せなものは何でも使ってください。


3

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

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

最初[]にクローンが作成されただけです。x.ly.lは両方とも邪悪な名前であり、同じ指示対象にリンクしている異なる即時値です。(言語弁護士の条件が構成されています)
Phlip 2015

1

クラス変数を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__


0

データクラスはPython 3.7で追加された機能、今クラスのインスタンスにデフォルト値を設定し達成するために、さらに別の(非常に便利)方法があります。デコレータdataclassは、コンストラクタなど、クラスにいくつかのメソッドを自動的に生成します。上記のリンク先のドキュメントに記載されているように、「これらの生成されたメソッドで使用するメンバー変数は、PEP526タイプのアノテーションを使用して定義されます」。

OPの例を考えると、次のように実装できます。

from dataclasses import dataclass

@dataclass
class Foo:
    num: int = 0

このクラスのタイプのオブジェクトを作成するときに、オプションで値を上書きできます。

print('Default val: {}'.format(Foo()))
# Default val: Foo(num=0)
print('Custom val: {}'.format(Foo(num=5)))
# Custom val: Foo(num=5)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.