1
1

More than 3 years have passed since last update.

[Python]オブジェクトの属性にアクセスしたときの挙動

Posted at

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__
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1