属性参照
通常の属性参照
x.nameでxの属性nameを参照した場合、以下の手順で検索される。
- xがインスタンスオブジェクトの場合
- 'name'がx.__dict__のキーである場合、x.nameはx.__dict__['name']の値を返す。
- それ以外の場合、x.nameは検索をxのクラスに委譲する(つまり、x.__class__.nameと同じように機能する)。
- xがクラスオブジェクトの場合
- 'name'がx.__dict__のキーである場合、x.nameはx.__dict__['name']の値を返す。
- それ以外の場合、x.nameは検索をCの基底クラスに委譲する(つまり、x.__bases__をループにかけ、それぞれごとにnameを検索する)
- 以上で見つからなかった場合
- 特殊メソッド__getattr__が定義もしくは継承されている場合、x.__getattr__['name']が呼び出される。__getattr__は、属性値を返すかAttributeError例外を発生させる必要がある。
- それ以外の場合、AttributeError例外が発生する。
- 新スタイルクラスで特殊メソッド__getattribute__が定義されている場合
- 上記のルールとは無関係にx.__getattribute__['name']が呼び出される。__getattribute__は、属性値を返すかAttributeError例外を発生させる必要がある。
>>> class B: ... a, b = 1, 2 ... def foo(self): ... print 'foo in B' ... def bar(self): ... print 'bar in B' ... >>> class D(B): ... b, c, d = 22, 33, 44 ... def bar(self): ... print 'bar in D' ... def baz(self): ... print 'baz in D' ... >>> x = D() >>> x.__dict__.keys() # インスタンスxの属性は存在しない [] >>> D.__dict__.keys() # クラスDの属性 ['__module__', 'b', 'bar', 'd', 'baz', 'c', '__doc__'] >>> B.__dict__.keys() # クラスBの属性 ['a', '__module__', 'b', 'bar', 'foo', '__doc__'] >>> x.foo # fooは参照可能 <bound method D.foo of <__main__.D instance at 0x00E3FB70>> >>> del x.foo # fooはxの属性でないので削除不可能 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: D instance has no attribute 'foo' >>> del B.foo # fooはBの属性なので削除可能 >>> x.foo # fooは削除されたので参照不可能 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: D instance has no attribute 'foo' >>> >>> del x.__dict__ # __dict__は削除できない Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __dict__ must be set to a dictionary >>> x.__dict__ = [] # __dict__は辞書以外の値は設定できない Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __dict__ must be set to a dictionary
__slots__属性
属性参照とは関係ないが、新スタイルクラスでは__slots__により属性を制限できる。
>>> class C(object): ... __slots__ = ['v1', 'v2'] ... >>> x = C() >>> x.v1 = 1 >>> x.v1, C.v1 (1, <member 'v1' of 'C' objects>) >>> x.v2 # まだ初期化していないので参照できない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: v2 >>> x.v3 = 3 # __slots__に定義していない属性には代入できない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'C' object has no attribute 'v3' >>> x.__dict__ # __slots__を定義すると、__dict__が生成されない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'C' object has no attribute '__dict__'
また、__slots__ 宣言が動作するのは、定義が行われたクラスだけに限られる。サブクラスでは、__slots__ を定義しない限り __dict__ を持つ。
ビルトインクラスの属性参照
ビルトインクラスは__dict__属性や__slots__属性を持っていないので、あらかじめ定義されている属性を参照することしかできない。新しい属性を追加したり、定義されている属性を削除することはできない。
>>> L = list() >>> L.__dict__ # __dict__属性は存在しない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'list' object has no attribute '__dict__' >>> L.x = 1 # 属性を追加できない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'list' object has no attribute 'x' >>> del L.sort # 定義されている属性を削除できない Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'list' object attribute 'sort' is read-only
継承すれば通常のクラスと同様に__dict__属性を持ち、自由に属性を制御できる。
>>> class List(list): pass ... >>> L = List() >>> L.__dict__ {} >>> L.x = 1