Python魔法の方法(五)から_get__,__set__, __delete__再プローブ属性アクセス順序
4560 ワード
ここではまず記述子の概念を述べる必要がある.
記述子:記述子の本質は新しいクラスであり、この新しいクラスでは、少なくとも__が実現される.get__(),__set__(),__delete__()の1つであり、これは記述子プロトコルとも呼ばれる.
記述子はデータ記述子に分けられ、__のみget__の記述子は非データ記述子であり、get__および_set__の記述子はデータ記述子です.
__get__(self,instance,owner)-プロパティを取得するときに呼び出され、設定されたプロパティ値が返されます.通常は_set_に表示されます. __set__(self,instance,value)-プロパティを設定するときに呼び出され、Noneに戻ります. __delete__(self,instance)-属性を削除するときに呼び出され、Noneに戻ります.
ここでinstanceは、この記述子属性が存在するクラスのインスタンスであり、ownerは記述子が存在するクラスである.
次の例を見てください
つまり、instanceとownerはクラスBのものです.
前編では、データ記述子がない場合、インスタンスアクセス属性の順序は次のようになります.
インスタンス属性a.nameにアクセスすると、そのアクセス順は、(データ記述子がないと仮定する)
先に入る_getattribute__
1、インスタンス属性a_dict__2、クラス属性A._dict__ 3、親およびベース属性A._bases__.__dict
検索できないgetattr__
データ記述子、または非データ記述子がある場合、アクセス順は次のようになります.
先に入る_getattribute__
1.属性がデータ記述子である場合get__2、インスタンス属性3、非データ記述子
検索できないgetattr__
データ記述子があり、依然として__に入ります.getattribute__,そして__へget__.データ記述子がない場合は、インスタンスプロパティに直接アクセスします.
理由:初期化_init__開始前から、データ記述子name、すなわちA(「Tom」)のインスタンス化が開始される.
Bのインスタンス化に入るとself.nameは記述子の付与操作が__に入るため、付与された.set__(ここでは実際には__set__は印刷操作のみを行い、instanceに値を付けていない).
インスタンスbの_dict__にnameという属性はなく,もちろんインスタンスbで所望の属性を得ることはできない.
Aの中の_をset__メソッドを書かない場合、nameは非データ記述子です.
たとえば
非データ記述子の優先度はインスタンス属性より低い.
関連用法:1、属性アクセス、修正制御
で_set__割り当て操作をブロックすると、不合理な値が間違って投げられます._delete__の使い方と_set__同様に、削除時に削除操作をブロックします.
データ記述子は@propertyと同様にプロパティアクセス制御の効果がありますが、データ記述子はより多くのプロパティ制御に使用でき、コードの煩雑さはありません.
注意すべき点:
1、書き換え_get__,__set__,__delete__デッドサイクルに入るのを避ける.
2、記述子が_getattribute()メソッド呼び出し、リロード_getattribute__()が適切でないと、記述子の自動呼び出しがブロックされます.
3、データ記述子はインスタンス辞書をリロードし、非データ記述子はインスタンス辞書にリロードされる可能性がある(属性アクセス順の理由).
記述子:記述子の本質は新しいクラスであり、この新しいクラスでは、少なくとも__が実現される.get__(),__set__(),__delete__()の1つであり、これは記述子プロトコルとも呼ばれる.
記述子はデータ記述子に分けられ、__のみget__の記述子は非データ記述子であり、get__および_set__の記述子はデータ記述子です.
__get__(self,instance,owner)-プロパティを取得するときに呼び出され、設定されたプロパティ値が返されます.通常は_set_に表示されます. __set__(self,instance,value)-プロパティを設定するときに呼び出され、Noneに戻ります. __delete__(self,instance)-属性を削除するときに呼び出され、Noneに戻ります.
ここでinstanceは、この記述子属性が存在するクラスのインスタンスであり、ownerは記述子が存在するクラスである.
次の例を見てください
class A(object):
name = "unchange"
def __init__(self, value):
print "into A __init__"
self.value = value
def __get__(self, instance, owner):
print "into __get__"
print instance,owner
class B(object):
value = A(10)
def __init__(self, value):
print "into B __init__"
b = B(20)
# into A __init__
# into B __init__
print b.value
# into __get__
# <__main__.b object="" at="">
# None
つまり、instanceとownerはクラスBのものです.
前編では、データ記述子がない場合、インスタンスアクセス属性の順序は次のようになります.
インスタンス属性a.nameにアクセスすると、そのアクセス順は、(データ記述子がないと仮定する)
先に入る_getattribute__
1、インスタンス属性a_dict__2、クラス属性A._dict__ 3、親およびベース属性A._bases__.__dict
検索できないgetattr__
データ記述子、または非データ記述子がある場合、アクセス順は次のようになります.
先に入る_getattribute__
1.属性がデータ記述子である場合get__2、インスタンス属性3、非データ記述子
検索できないgetattr__
class A(object):
def __init__(self, name):
print "into A __init__"
self.name = name
def __get__(self, instance, owner):
print "into __get__"
def __set__(self, instance, value):
print "into __set__"
class B(object):
name = A("Tom") # , “Bob”
def __init__(self, name, age):
print "into B __init__"
self.age = age
self.name = name
def __getattribute__(self, item):
print "into __getattribute__"
return object.__getattribute__(self, item)
b = B("Bob", 100)
# into A __init__
# into B __init__
# into __set__
print b.name
# into __getattribute__
# into __get__
# None
データ記述子があり、依然として__に入ります.getattribute__,そして__へget__.データ記述子がない場合は、インスタンスプロパティに直接アクセスします.
理由:初期化_init__開始前から、データ記述子name、すなわちA(「Tom」)のインスタンス化が開始される.
Bのインスタンス化に入るとself.nameは記述子の付与操作が__に入るため、付与された.set__(ここでは実際には__set__は印刷操作のみを行い、instanceに値を付けていない).
インスタンスbの_dict__にnameという属性はなく,もちろんインスタンスbで所望の属性を得ることはできない.
Aの中の_をset__メソッドを書かない場合、nameは非データ記述子です.
たとえば
class A(object):
def __init__(self, name):
print "into A __init__"
self.name = name
def __get__(self, instance, owner):
print "into __get__"
class B(object):
name = A("Tom") # , “Bob”
def __init__(self, name, age):
print "into B __init__"
self.age = age
self.name = name
def __getattribute__(self, item):
print "into __getattribute__"
return object.__getattribute__(self, item)
b = B("Bob", 100)
# into A __init__
# into B __init__
# into __set__
print b.name
# into __getattribute__
# Bob
非データ記述子の優先度はインスタンス属性より低い.
関連用法:1、属性アクセス、修正制御
class A(object):
def __init__(self, name):
print "into A __init__"
self.name = name
def __get__(self, instance, owner):
print "into __get__"
return instance.__dict__[self.name]
def __set__(self, instance, value):
print "into __set__"
if value < 0:
raise ValueError
instance.__dict__[self.name] = value
class B(object):
name = A("name")
age = A("age")
def __init__(self, name, age):
print "into B __init__"
self.age = age
self.name = name
def __getattribute__(self, item):
print "into __getattribute__"
return object.__getattribute__(self, item)
b = B("Bob", 20)
b.age = -1
print b.age
で_set__割り当て操作をブロックすると、不合理な値が間違って投げられます._delete__の使い方と_set__同様に、削除時に削除操作をブロックします.
データ記述子は@propertyと同様にプロパティアクセス制御の効果がありますが、データ記述子はより多くのプロパティ制御に使用でき、コードの煩雑さはありません.
注意すべき点:
1、書き換え_get__,__set__,__delete__デッドサイクルに入るのを避ける.
2、記述子が_getattribute()メソッド呼び出し、リロード_getattribute__()が適切でないと、記述子の自動呼び出しがブロックされます.
3、データ記述子はインスタンス辞書をリロードし、非データ記述子はインスタンス辞書にリロードされる可能性がある(属性アクセス順の理由).