Pythonの属性にアクセスしたときの挙動がこの記事にまとめられていたのですが、忘れそうなのでコードの形式で整理しておきます。
class MyGetDescriptor:
def __init__(self, msg):
self.__msg = msg
def __set_name__(self, owner, name):
self.__name = name
def __get__(self, instance, owner):
return self.__name + " " + self.__msg
def __set__(self, instance, value): # これは必要
pass
class ClassA:
get1 = MyGetDescriptor("A Descriptor")
get2 = MyGetDescriptor("A Descriptor")
get3 = MyGetDescriptor("A Descriptor")
get4 = MyGetDescriptor("A Descriptor")
get5 = MyGetDescriptor("A Descriptor")
get6 = MyGetDescriptor("A Descriptor")
get7 = "get7 ClassA"
def __getattr__(self, name):
if name in ["get1", "get2", "get3", "get4", "get5",
"get6", "get7", "get8", "get9"]:
return name + " A.__getattr__"
else:
return super(self.__class__, self).__getattr__(name)
def __getattribute__(self, attr):
if attr == "get1":
return "get1 A.__getattribute__"
elif attr == "get2":
return "get2 A.__getattribute__"
else:
return super(ClassA, self).__getattribute__(attr)
class ClassB(ClassA):
get1 = MyGetDescriptor("B Descriptor")
get2 = MyGetDescriptor("B Descriptor")
get3 = MyGetDescriptor("B Descriptor")
get4 = "get4 ClassB"
get5 = "get5 ClassB"
def __init__(self):
self.get1 = "get1 InstanceB"
self.get2 = "get2 InstanceB"
self.get3 = "get3 InstanceB"
self.get4 = "get4 InstanceB"
def __getattr__(self, name):
if name in ["get1", "get2", "get3", "get4", "get5",
"get6", "get7", "get8"]:
return name + " B.__getattr__"
else:
return super(self.__class__, self).__getattr__(name)
def __getattribute__(self, attr):
if attr == "get1":
# ここでself.__dict__を参照すると__getattribute__(self, '__dict__')が呼ばれる)
return "get1 B.__getattribute__"
else:
return super(ClassB, self).__getattribute__(attr)
# super(ClassB, self)をsuper(self.__class__, self)にするとスタックオーバーフロー
b = ClassB()
print(b.get1) # get1 B.__getattribute__
print(b.get2) # get2 A.__getattribute__
print(b.get3) # get3 B Descriptor
print(b.get4) # get4 InstanceB
print(b.get5) # get5 ClassB
print(b.get6) # get6 A Descriptor
print(b.get7) # get7 ClassA
print(b.get8) # get8 B.__getattr__
print(b.get9) # get9 A.__getattr__
print(ClassB.get5) # ClassB
print(ClassB.get6) # A Descriptor
よって、属性を取得するときは以下の順番で確認していることがわかります。
- 該当クラスの
__getattribute__
- 親クラスの
__getattribute__
- Descriptorを用いた該当クラスのメンバ
- 該当インスタンスのメンバ
- 該当クラスのメンバ
- - Descriptorを用いた親クラスのメンバ
- 親クラスのメンバ
- 該当クラスの
__getattr__
- 親クラスの
__getattr__