144

次の間に意味のある違いはありますか?

class A(object):
    foo = 5   # some default value

対。

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

多くのインスタンスを作成している場合、2つのスタイルのパフォーマンスまたはスペース要件に違いはありますか?コードを読むとき、2つのスタイルの意味が大幅に異なると思いますか?

4

5 に答える 5

158

重要なセマンティックの違いがあります (パフォーマンスの考慮事項を超えて):

  • インスタンスで属性が定義されている場合(これは通常行うことです)、複数のオブジェクトが参照される可能性があります。それぞれがその属性の完全に個別のバージョンを取得します
  • class で属性が定義されている場合、参照される基礎となるオブジェクトは 1 つだけです。そのため、そのクラスの異なるインスタンスに対する操作が両方とも属性を設定/(追加/拡張/挿入/など)しようとすると、次のようになります。
    • 属性が組み込み型(int、float、boolean、string など) の場合、1 つのオブジェクトに対する操作によって値が上書き (破壊) されます。
    • 属性が変更可能なタイプ(リストや辞書など) の場合、不要なリークが発生します。

例えば:

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]
于 2008-10-16T01:26:53.000 に答える
42

違いは、クラスの属性がすべてのインスタンスで共有されることです。インスタンスの属性は、そのインスタンスに固有です。

C++ からの場合、クラスの属性は静的メンバー変数に似ています。

于 2008-10-16T08:16:28.370 に答える
37

これは非常に良い投稿であり、以下に要約します。

class Bar(object):
    ## No need for dot syntax
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

そして視覚的な形で

ここに画像の説明を入力

クラス属性の割り当て

  • クラスにアクセスしてクラス属性を設定すると、すべてのインスタンスの値が上書きされます

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • インスタンスにアクセスしてクラス変数を設定すると、そのインスタンスの値のみがオーバーライドされます。これは基本的にクラス変数をオーバーライドし、直感的にそのインスタンスでのみ使用可能なインスタンス変数に変換します。

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

クラス属性はいつ使用しますか?

  • 定数の格納。クラス属性はクラス自体の属性としてアクセスできるため、クラス全体のクラス固有の定数を格納するためにそれらを使用すると便利なことがよくあります。

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
    
  • デフォルト値の定義。些細な例として、制限付きリスト (つまり、特定の数以下の要素しか保持できないリスト) を作成し、デフォルトの上限を 10 アイテムにすることを選択する場合があります。

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10
    
于 2015-12-07T03:59:08.480 に答える
21

ここのコメントと、重複としてマークされた他の2つの質問の人々はすべて、これについて同じように混乱しているように見えるので、Alex Coventryの.

アレックスがリストのような可変型の値を割り当てているという事実は、物事が共有されているかどうかとは何の関係もありません。これは、id関数またはis演算子で確認できます。

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

(なぜ私がobject()の代わりにを使用したのか疑問に思っているなら5、それは、ここでは触れたくない他の 2 つの問題に遭遇するのを避けるためです。2 つの異なる理由により、完全に個別に作成され5た が最終的にnumber の同じインスタンス5。ただし、完全に個別に作成されたobject()はできません。)


では、なぜa.foo.append(5)Alex の例では に影響するb.fooのにa.foo = 5、私の例では影響しないのでしょうか? さて、アレックスの例を試してみてください。そこに影響がa.foo = 5ないことに注意してください。b.foo

a.foo = 5a.fooの名前になっているだけです5b.fooこれは、 や、以前参照していた古い値の他の名前には影響しませんa.foo。* クラス属性を隠すインスタンス属性を作成するのは少し難しいです**。ここで起こっています。


アレックスがリストを使用した理由が明らかになったといいのですが: リストを変更できるということは、2 つの変数が同じリストに名前を付けていることを示すのが簡単であることを意味します。同じリストの 2 つの名前。


* C++ のような言語を使用している人が混乱するのは、Python では値が変数に格納されないことです。値はそれ自体で値の土地に住んでいます。変数は値の名前にすぎず、代入は値の新しい名前を作成するだけです。それが役立つ場合は、各 Python 変数をshared_ptr<T>ではなく と考えてTください。

** 一部の人々は、クラス属性を、インスタンスが設定する場合と設定しない場合があるインスタンス属性の「デフォルト値」として使用することで、これを利用しています。これは場合によっては便利ですが、混乱を招く可能性もありますので注意してください。

于 2014-10-29T23:32:51.803 に答える