pythonプロパティ管理(1):ベース

8321 ワード

属性を管理するいくつかの方法
pythonでオブジェクトのプロパティにアクセス、設定、削除するには、次の方法があります.
  • 組み込み関数getattr()、setattr()およびdelattr()
  • を使用
  • 独自に作成するgetter()setter()deleter()の方法
  • .
  • は、__getattr__()__setattr__()__delattr__()の演算子をリロードし、x.yのアクセス、付与方式、およびdel x.yの方式
  • を決定する.
  • __getattribute__()
  • を用いる.
  • ディスクリプタプロトコル
  • を使用
  • propertyプロトコルを使用します.これは特殊な記述子
  • です.
    本文ではその中の上位4種を簡単に紹介し,後2種を単独の文章で説明する.
    組み込み関数XXXXattr()管理プロパティ
    組み込み関数getattr()、setattr()、delattr()を使用すると、オブジェクトのプロパティに簡単にアクセス、設定、削除できます.
    ヘルプドキュメントを参照してください.
    getattr(...)
        getattr(object, name[, default]) -> value
        Get a named attribute from an object;
        getattr(x, 'y') is equivalent to x.y.
    
    setattr(obj, name, value, /)
        Sets the named attribute on the given object to the specified value.
        setattr(x, 'y', v) is equivalent to ``x.y = v''
    
    delattr(obj, name, /)
        Deletes the named attribute from the given object.
        delattr(x, 'y') is equivalent to ``del x.y''

    操作するオブジェクトobjと操作する属性名nameを指定するのは簡単です.getattr()の場合、操作する属性が存在しない場合はデフォルトでエラーが報告されます.defaultパラメータが属性が存在しないことを示すときに、その属性値を返すことができます.
    たとえば、以下は簡単なPersonクラスとオブジェクトpです.
    class Person():
        def __init__(self, name):
            self.name = name
    
    p = Person("malongshuai")

    getattr()を使用して、nameプロパティと存在しないageプロパティを取得します.
    print(getattr(p, "name"))
    print(getattr(p, "age", 23))

    上からage属性にアクセスすると、3番目のパラメータ「23」を削除すると異常が放出されます.
    AttributeError: 'Person' object has no attribute 'age'

    setattr()とdelattr()を使用して、プロパティを設定および削除します.
    setattr(p, "age", 25)
    print(p.__dict__)
    
    delattr(p, "age")
    print(p.__dict__)

    結果を返します.
    {'name': 'malongshuai', 'age': 25}
    {'name': 'malongshuai'}

    自己作成アクセスメソッド
    一般的にオブジェクト向けの言語は、自分でsetter、getter、deleterメソッドを書いて属性を管理するもので、汎用的で安全ですが、管理するのはそんなに便利ではありません.
    ここでは、pythonがオブジェクトのプロパティを設定することを参照してください.
    たとえば、Personクラスにname、ageの2つのプロパティのaccessorメソッドを追加します.
    class Person():
        def __init__(self, name):
            self.name = name
    
        def set_name(self,name): self.name = name
        def get_name(self): return self.name
        def del_name(self): del self.name
    
        def set_age(self,age): self.age = age
        def get_age(self): return self.age
        def del_age(self): del self.age

    欠点は明らかで、管理したい各属性に対して、これらの属性を定義しなければならない.すなわち,accessorメソッドは単一の属性に対するものである.
    演算子リロード管理属性
    通常、ポイント演算を直接使用してプロパティにアクセスしたり、設定したりすることができます.例:
    p.name          # (1)  p   name  
    p.name = "abc"  # (2) p   name    
    del p.name      # (3)  p   name  

    まず,オブジェクトの付与と削除操作,すなわち上の(2)と(3)である.この2つの操作は、__setattr__()__delattr__()の2つの方法によって直接ブロックされるか、あるいは、この2つの方法が書き換えられさえすれば、属性に値を付与し、削除するたびに、対応する2つの方法が呼び出される.
    さらに、属性にアクセスする操作(1)について、pythonは、2つの対応する方法__getattr__()および__getattribute__()を提供する.前者は存在しない属性にアクセスするときに自動的に呼び出され、後者は属性にアクセスするときに呼び出され、属性が存在するかどうかを無視します.
    ここでは、後で発生する問題のまとめを先に説明します.すべての属性操作に適用される__setattr____delattr____getattribute__の方法では、それらの無限の再帰を避ける必要があります.以下の例を参照すれば分かる.
    __getattr__() __getattr__()は、存在しない属性にドットアクセスすることによって呼び出される.プロパティ値を返すか、例外を放出するかの2つの使用基準があります.
    例:
    class Person():
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, attrname):
            if attrname == "name":
                print("in getattr1")
                return self.name
            elif attrname == "age":
                print("in getattr2")
                return 25
            else:
                print("in getattr3")
                raise AttributeError(attrname)
    
    p = Person("malongshuai")

    上記のPersonクラスには属性nameがあるため、name属性にアクセスすると__getattr__()は呼び出されず、ageまたは他の属性にアクセスするとメソッドが呼び出されますが、age属性にはカスタムの戻り値があり、他の属性はエラーを報告します.
    print(p.name)
    print(p.age)
    print(p.job)

    出力結果は次のとおりです.
    malongshuai
    in getattr2
    25
    in getattr3
    Traceback (most recent call last):
      File "g:/pycode/b.py", line 21, in 
        print(p.job)
      File "g:/pycode/b.py", line 14, in __getattr__
        raise AttributeError(attrname)
    AttributeError: job

    __getattribute__() __getattribute__()__getattr__()は同様であり、ターゲット属性が存在するかどうかにかかわらず、前者はすべての属性へのアクセスに適している.__getattribute__()は、すべての属性アクセス操作に適用されるため、無限の再帰を避ける必要があることに注意してください.たとえば、次は間違った書き方です.
    def __getattribute__(self, attr):
        return self.attr

    この方法のself.attrは、__getattribute__の呼び出しをトリガし続け、無限再帰問題が発生するからである.
    解決策は、super()やobjectクラスなどの親クラスからアクセスすることです.
    super().__getattribute__(attr)
    object.__getattribute__(self, attr)
    __getattribute__()の優先度は__getattr__()より高く、前者が存在する場合、前者のコードで後者が呼び出されていないか、前者が異常を投げ出さない限り、後者は呼び出されません.
    例:
    class Person():
        def __init__(self, name):
            self.name = name
    
        def __getattribute__(self, attr):
            print("in getattribute")
            return object.__getattribute__(self, attr)
            # return super.__getattribute__(attr)
    
        def __getattr__(self, attrname):
            if attrname == "name":
                print("in getattr1")
                return self.name
            elif attrname == "age":
                print("in getattr2")
                return 25
            else:
                print("in getattr3")
                raise AttributeError(attrname)
    
    
    p = Person("malongshuai")
    
    print(p.name)
    print(p.age)

    結果を返します.
    in getattribute
    malongshuai
    in getattribute
    in getattr2
    25

    nameとageの2つの属性が出力されたが、「p.age」が出力されるとその属性は存在せず、__getattribute__が異常を放出し、__getattr__がトリガーされる.
    無限再帰問題を解決する上で、後の__setattr__および__delattr__には、__dict__にアクセスする方法もある.これは、この辞書へのアクセスが__getattribute__をトリガし、無限再帰を継続するため、ここの__getattribute__には適していない.
    __setattr__() __setattr__()は、オブジェクト属性付与動作をブロックするために使用される.例:
    p.name = "long"

    呼び出しp.__setattr__(self,name,"long")に変換されます.
    唯一注意しなければならないのは、付与時の無限再帰問題を避けることです.__setattr__()における付与文self.attr = valueは、このメソッドを呼び出し続け、最終的には無限再帰をもたらすからである.
    したがって、__setattr__()メソッドでは、__dict__を使用して属性を取得し、付与するか、親の同名属性にアクセスする必要があります.したがって、無限再帰呼び出しを回避するには、次のいくつかの方法があります.
    self.__dict__[attr] = value
    super().__setattr__(attr, value)
    object.__setattr__(self, attr, value)

    以下の例を参照してください.
    class Person():
        def __init__(self, name):
            self.name = name
    
        def __setattr__(self, attr, value):
            print("in setattr")
            #self.__dict__[attr] = value
            #super().__setattr__(attr, value)
            object.__setattr__(self, attr, value)
    
    p = Person("malongshuai")
    
    p.age = 33      #     __setattr__()
    print(p.age)

    実行結果:
    in setattr
    in setattr
    33

    問題が発見された可能性があります.in setterの値付け操作も__init__()をトリガーするため、__setattr__()が2回出力されます.
    __delattr__() del x.yが呼び出されると、__delattr__()の呼び出しが自動的にトリガーされます.
    同様に注意すべきは,付与時の無限再帰問題を回避することである.__delattr__()のdel文は、メソッドを呼び出し続ける可能性があるため、最終的には無限再帰をもたらす可能性があります.したがって、__delattr__()メソッドでは、__dict__を使用して属性を取得し、付与するか、親の同名属性にアクセスする必要があります.したがって、無限再帰呼び出しを回避するには、次のいくつかの方法があります.
    del self.__dict__[attr]
    super().__delattr__(attr)
    object.__delattr__(self, attr)

    例:
    class Person():
        def __init__(self, name):
            self.name = name
    
        def __delattr__(self, attr):
            print("%s deleting" % (attr))
    
            #del self.__dict__[attr]
            #super().__delattr__(attr)
            object.__delattr__(self, attr)
    
            print("%s deleted" % (attr))
    
    
    p = Person("malongshuai")
    p.age = 33
    del p.age