次の間に意味のある違いはありますか?
class A(object):
foo = 5 # some default value
対
class B(object):
def __init__(self, foo=5):
self.foo = foo
多数のインスタンスを作成している場合、2つのスタイルのパフォーマンスまたはスペース要件に違いはありますか?コードを読むとき、2つのスタイルの意味は大きく異なると思いますか?
次の間に意味のある違いはありますか?
class A(object):
foo = 5 # some default value
対
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
[]
int
、str
彼らはまだクラスではなく各インスタンスに接続されています。
int
とはstr
されても、全く同じ方法で、共有しました。is
またはで簡単に確認できid
ます。または、各インスタンス__dict__
とクラスのを調べます__dict__
。不変の型が共有されているかどうかは、通常はそれほど重要ではありません。
a.foo = 5
と、どちらの場合でもb.foo
return が表示されます[]
。これは、最初のケースでa.foo
は、同じ名前の新しいインスタンス属性でクラス属性を上書きしているためです。
違いは、クラスの属性がすべてのインスタンスで共有されることです。インスタンスの属性は、そのインスタンスに固有です。
C ++からの場合、クラスの属性は静的メンバー変数に似ています。
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
値変異されa.foo
ている間、を参照しa.foo = 5
つつあるa.foo
値の新しい名前に5
。つまり、クラス属性を非表示にするインスタンス属性になります。a.foo = 5
Alexのバージョンでも同じことを試してみてくださいb.foo
。変更されていないことがわかります。
ここに非常に良い投稿があります、そして以下のように要約してください。
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
ここのコメントと、重複としてマークされた他の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つの問題に遭遇するのを避けるためです。2つの異なる理由で、完全に別々に作成された5
が最終的に同じ数のインスタンスです5
。ただし、完全に個別に作成されたobject()
はできません。)
では、なぜa.foo.append(5)
Alexの例ではb.foo
が影響するのa.foo = 5
ですか?私の例では影響しないのですか?さて、a.foo = 5
アレックスの例を試してみてください、そしてそれはb.foo
そこにも影響を与えないことに注意してください。
a.foo = 5
のa.foo
名前にしてい5
ます。これはb.foo
、や、以前a.foo
は参照されていた古い値のその他の名前には影響しません。*クラス属性を非表示にするインスタンス属性を作成するのは少し難しいですが、一度取得すると、複雑なことは何もありません。ここで起こっています。
うまくいけば、Alexがリストを使用した理由は明らかです。リストを変更できるという事実は、2つの変数が同じリストに名前を付けていることを示すのが簡単であること、および2つのリストがあるかどうかを知ることが実際のコードでより重要であることを意味します同じリストの2つの名前。
* C ++などの言語を使用している人の混乱は、Pythonでは値が変数に格納されないことです。値は値域に存在し、それ自体で、変数は値の単なる名前であり、割り当ては値の新しい名前を作成するだけです。それが役立つ場合は、各Python変数をのshared_ptr<T>
代わりにと考えてくださいT
。
**一部の人々は、インスタンスが設定する場合と設定しない場合があるインスタンス属性の「デフォルト値」としてクラス属性を使用することにより、これを利用します。これは場合によっては便利ですが、混乱する可能性もありますので、注意してください。
もう1つ状況があります。
クラスとインスタンスの属性はDescriptorです。
# -*- 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の定義と素晴らしい投稿で見つけました。
属性が、構成するクラスの辞書で見つかった場合。オブジェクトMRO、次に、ルックアップされている属性がデータ記述子を指しているかどうかを確認します(これは
__get__
と__set__
メソッドの両方を実装するクラスにすぎません)。存在する場合__get__
は、データ記述子のメソッドを呼び出して(28行目から33行目)、属性ルックアップを解決します。