質問

次の間に意味のある区別はありますか

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

vs。

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

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

役に立ちましたか?

解決

パフォーマンスの考慮事項以外に、セマンティックには大きな違いがあります。クラス属性の場合、参照されるオブジェクトは1つだけです。 instance-attribute-set-at-instantiationでは、複数のオブジェクトを参照できます。たとえば

>>> 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    
[]

他のヒント

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

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

これは非常に優れた投稿です。以下。

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

そして視覚的な形で

enter画像の説明はこちら

クラス属性の割り当て

  • クラスにアクセスしてクラス属性を設定すると、すべてのインスタンス

    の値が上書きされます
    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
    

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

Alexがリストのような可変型の値を割り当てているという事実は、物事が共有されているかどうかとは関係ありません。これは、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つの問題にぶつからないようにします。理由として、完全に個別に作成されたa.foo.append(5) sは、番号b.fooの同じインスタンスになる可能性がありますが、完全に個別に作成されたa.foo = 5 sはできません。)


では、なぜアレックスの例のa.fooshared_ptr<T>に影響するのに、私の例のTは影響しないのですか?さて、Alexの例で<=>を試してみてください。これは のどちらにも影響しないことに注意してください。

<=>は、単に<=>を<=>の名前にしています。 <=>、または<=>が参照していた古い値の他の名前には影響しません。*クラス属性を非表示にするインスタンス属性を作成するのは少し難しいですが、ここで複雑なことは何も起きていません。


Alexがリストを使用した理由が明らかになった:リストを変更できるという事実は、2つの変数が同じリストに名前を付けていることを示すのが簡単であり、実際のコードでは、 2つのリストまたは同じリストの2つの名前。


* C ++のような言語から来た人の混乱は、Pythonでは値が変数に保存されないことです。値はバリューランドに存在し、それ自体では、変数は値の単なる名前であり、割り当ては値の新しい名前を作成するだけです。役立つ場合は、各Python変数を<=>ではなく<=>と考えてください。

**一部の人々は、クラス属性を<!> quot; default value <!> quot;として使用することでこれを利用しています。インスタンスが設定する場合としない場合があるインスタンス属性の場合。これは場合によっては便利ですが、混乱を招く可能性があるため、注意してください。

もう1つの状況があります。

クラスおよびインスタンスの属性は記述子です。

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

上記の出力:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

クラスまたはインスタンスを介した同じタイプのインスタンスアクセスは、異なる結果を返します!

そして c.PyObject_GenericGetAttr定義 <!>#65292;および素晴らしい投稿

説明

  

属性が、構成するクラスの辞書で見つかった場合。   オブジェクトMRO。次に、ルックアップされる属性がデータ記述子を指しているかどうかを確認します(これは__get__メソッドと__set__メソッドの両方を実装するクラスにすぎません)。   存在する場合は、データ記述子の<=>メソッドを呼び出して属性検索を解決します(行28 <!>#8211; 33)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top