Python descriptor(描述符)的實(shí)現(xiàn)
問題
問題1
Python是一種動(dòng)態(tài)語言,不支持類型檢查。當(dāng)需要對(duì)一個(gè)對(duì)象執(zhí)行類型檢查時(shí),可能會(huì)采用下面的方式:
class Foo(object):
def __init__(self,a):
if isinstance(a,int):
self.__a = a
else:
raise TypeError("Must be an int")
def set_a(self,val):
if isinstance(val,int):
self.__a = val
else:
raise TypeError("Must be an int")
def get_a(self):
return self.__a
上述是一種類型檢查的方法,但是如果需要類型檢查的參數(shù)非常多的話就會(huì)變得非常繁瑣,重復(fù)代碼太多,Python這么簡(jiǎn)潔,優(yōu)雅,肯定有更好的解決方法。另外上述方法也有缺陷。
f = Foo(1) print f.get_a() #1 print f._Foo__a #1,還是能訪問到= = f._Foo__a = 'test' print f.get_a() #test,還是改變了__a的值,而且不是int型 print f._Foo__a #test
問題2
在一個(gè)對(duì)象中,創(chuàng)建一個(gè)只讀屬性。
問題3
class Foo(object):
a = 1
f = Foo()
print f.a #1,實(shí)例屬性中沒有a屬性,所以到Foo.__dict__中查找
print Foo.a #1
print f.__dict__ #{}
f.a = 2 #增加一個(gè)名為a的實(shí)例屬性
print f.a #2,搜索屬性時(shí)先在實(shí)例字典中查找,然后再去類的字典中查找,在實(shí)例的__dict__中找到了..
print Foo.a #1
print f.__dict__ #{'a': 2}
如果不想給實(shí)例f增加實(shí)例屬性,而是想對(duì)類屬性操作怎么辦呢。解決方案:
1) 使用class.attr改變值;
Foo.a = 2就不會(huì)給實(shí)例增加實(shí)例屬性了。
2) 自定義屬性訪問,描述符;
class descriptor(object):
def __init__(self,val):
self.val = val
def __get__(self,obj,type = None):
print 'get',
return self.val
def __set__(self,obj,val):
print 'set',val
self.val = val
def __delete__(self,obj):
raise AttributeError("Can't delete attribute")
class Foo(object):
a = descriptor(0)
f = Foo()
print f.a #get 0
print Foo.a #get 0
print f.__dict__ #{}
f.a = 2 #set 2,并沒有增加實(shí)例屬性
print f.a #get 2
print Foo.a #get 2
print f.__dict__ #{}
問題總結(jié)
上述三個(gè)問題均與屬性訪問有關(guān),如果能夠自定義屬性訪問,上述問題就能解決啦= =。其實(shí)問題3已經(jīng)給出了解決方法,就是描述符…
描述符的定義和介紹
描述符(Descriptor)是Python中非常重要的一部分,它廣泛應(yīng)用于Python的內(nèi)核。
一般來說,描述符就是一個(gè)帶有綁定方法的對(duì)象,只不過照比其他對(duì)象多了幾個(gè)特殊的描述符方法,即 __get__(),__set__(),__delete__() 。對(duì)描述符對(duì)象的屬性訪問主要通過描述符方法。
定義:一個(gè)對(duì)象如果定義了
__get__(),__set__(),__delete__()方法中的任何一個(gè),它就可以被稱為描述符。
對(duì)屬性的訪問默認(rèn)是從對(duì)象的字典中獲取(get),設(shè)置(set)和刪除(delete)屬性。
假設(shè)有實(shí)例a,a有屬性x,獲取a.x的值。
一般來說,
a.x會(huì)按以下順序查找屬性,查找鏈:a.__dict__['x']→type(a).__dict__['x']→根據(jù)mro順序在type(a)的父類中查找(不包括元類)。
但是,如果被查找的屬性是一個(gè)描述符,并且為類屬性,那么就會(huì)覆蓋其默認(rèn)行為,轉(zhuǎn)而去調(diào)用描述符方法。注意,描述符僅適用于新式類和新式對(duì)象。
class descriptor(object):
def __init__(self,val):
self.val = val
def __get__(self, obj, objtype):
print 'get',
return self.val
def __set__(self, obj, val):
print 'set'
self.val = val
class Foo(object):
x = descriptor(0)
y = 0
f = Foo()
'''描述符覆蓋默認(rèn)的訪問行為'''
print f.x #get 0,調(diào)用的是描述符的__get__函數(shù)
print f.__dict__ #{}
f.x = 1 #set,調(diào)用的是描述符的__set__函數(shù)
print f.x #get 1
print f.__dict__ #{},即使賦值也沒有增加實(shí)例屬性...,是不是覆蓋了默認(rèn)行為- -
f.y = 2
print f.__dict__ #{'y': 2},因?yàn)闆]有與y同名的描述符對(duì)象...
描述符十分強(qiáng)大。properties,methods, static methods,class methods, and super()都是基于描述符實(shí)現(xiàn)的。從Python2.2開始,描述符就在新式類中出現(xiàn)了。描述符是一個(gè)靈活的工具,使程序開發(fā)更加便利。感覺描述符很吊啊…
Descriptor Protocol(描述符協(xié)議)
descriptor.__get__(self, obj, type=None) --> value
descriptor.__set__(self, obj, value) --> None
descriptor.__delete__(self, obj) --> None
上述三個(gè)方法就是描述符方法,如果一個(gè)對(duì)象定義了描述符方法中的任何一個(gè),那么這個(gè)對(duì)象就會(huì)成為描述符。
假設(shè)有一個(gè)對(duì)象t,t.a是一個(gè)定義了三個(gè)描述符方法的描述符,并且a是類屬性,調(diào)用情況如下:
print t.a → a.__get__(t, type(t))
t.a = v → a.__set__(t, v)
del t.a → a.__delete__(t)
注意:當(dāng)描述符作為類屬性時(shí),才會(huì)自動(dòng)調(diào)用描述符方法,描述符作為實(shí)例屬性時(shí),不會(huì)自動(dòng)調(diào)用描述符方法。
數(shù)據(jù)和非數(shù)據(jù)描述符
data descriptor(數(shù)據(jù)描述符):定義了
__get__()和__set__()方法的對(duì)象;non-data descriptor(非數(shù)據(jù)描述符):僅定義了
__get__()的對(duì)象;非數(shù)據(jù)描述符典型應(yīng)用是class methods。
數(shù)據(jù)和非數(shù)據(jù)描述符的區(qū)別
如果一個(gè)實(shí)例的字典(__dict__)中有一個(gè)和數(shù)據(jù)描述符對(duì)象相同名字的實(shí)例屬性,則優(yōu)先訪問數(shù)據(jù)描述符;
class descriptor(object):
def __init__(self,val = 0):
self.val = val
def __get__(self, obj, objtype):
print '__get__',
return self.val
def __set__(self, obj, val):
self.val = val
class Foo(object):
a = descriptor()#數(shù)據(jù)描述符
f = Foo()
f.a = 1 #不會(huì)增加實(shí)例屬性,會(huì)調(diào)用descriptor.__set__方法,數(shù)據(jù)描述符的優(yōu)先級(jí)比實(shí)例屬性高
print f.a #__get__ 1,調(diào)用的是descriptor.__get__方法
print f.__dict__ #{}
如果一個(gè)實(shí)例中存在和數(shù)據(jù)描述符名字相同的實(shí)例屬性,利用下述方法就可以訪問實(shí)例屬性…
f.__dict__['a'] = 2 #增加一個(gè)與數(shù)據(jù)描述符同名的實(shí)例屬性
print f.__dict__ #{'a': 2}
print f.a #__get__ 1,正常使用,調(diào)用的仍是描述符的__get__方法
print f.__dict__['a'] #2,這個(gè)時(shí)候就是實(shí)例屬性了...
如果一個(gè)實(shí)例的字典中有一個(gè)和非數(shù)據(jù)描述符對(duì)象相同名字的實(shí)例屬性,則優(yōu)先訪問實(shí)例屬性;
class descriptor(object):
def __init__(self,val = 0):
self.val = val
def __get__(self, obj, objtype):
print '__get__',
return self.val
class Foo(object):
a = descriptor()#非數(shù)據(jù)描述符,沒有__set__
f = Foo()
print f.a #__get__ 0,調(diào)用descriptor.__get__方法
f.a = 1 #增加實(shí)例屬性,因?yàn)閷?shí)例屬性的優(yōu)先級(jí)比非數(shù)據(jù)描述符高
print f.a #1,不會(huì)調(diào)用descriptor.__get__方法,實(shí)例屬性的優(yōu)先級(jí)比非數(shù)據(jù)描述符高
print f.__dict___ #{'a': 1},增加了實(shí)例屬性a
數(shù)據(jù)和非數(shù)據(jù)描述符測(cè)試code
class data_descriptor(object):#數(shù)據(jù)描述符
def __init__(self,val):
self.val = val
def __get__(self, obj,type = None):
print 'data get'
return self.val
def __set__(self, obj, value):
if not isinstance(value,int):#賦值時(shí)可以類型檢查啊
raise ValueError('value must be int')
self.val = value
class non_data_descriptor(object):#非數(shù)據(jù)描述符
def __init__(self,val):
self.val = val
def __get__(self,obj,type = None):
print 'non data get'
return self.val
class Foo(object):
data = data_descriptor(0)
non_data = non_data_descriptor(1)
f = Foo()
print f.__dict__ #{}
print f.data #data get,0
print f.non_data #non data get,1
f.data = 2 #數(shù)據(jù)描述符優(yōu)先級(jí)較高,不會(huì)創(chuàng)建實(shí)例屬性,而是調(diào)用描述符的__set__方法
f.non_data = 3 #增加了與非數(shù)據(jù)描述符同名的實(shí)例屬性
print f.__dict__ #{'non_data': 3}
print f.data #data get,2 調(diào)用數(shù)據(jù)描述符的__get__()方法
print f.non_data #3,非數(shù)據(jù)描述符優(yōu)先級(jí)比實(shí)例屬性低
print Foo.non_data #non data get 1,利用類屬性查找還是可以訪問非數(shù)據(jù)描述符的,非數(shù)據(jù)描述符值未改變
屬性訪問,__getattribute__()
在Python中,__getattribute__()就是屬性解析機(jī)制,當(dāng)調(diào)用一個(gè)屬性時(shí),不管是成員還是方法,都會(huì)觸發(fā) __getattribute__()來調(diào)用屬性。
屬性解析機(jī)制按照優(yōu)先級(jí)鏈搜索屬性。在優(yōu)先級(jí)鏈中,類字典中的數(shù)據(jù)描述符的優(yōu)先級(jí)高于實(shí)例變量,實(shí)例屬性的優(yōu)先級(jí)高于非數(shù)據(jù)描述符,如果定義了__getattr()__,優(yōu)先級(jí)鏈會(huì)為__getattr()__分配最低優(yōu)先級(jí)。可以通過自定義__getattribute__方法來重寫優(yōu)先級(jí)鏈。
優(yōu)先級(jí)鏈 : 數(shù)據(jù)描述符 > 實(shí)例屬性 > (非數(shù)據(jù)描述符,非描述符的類屬性) >
__getattr()__上述所說的描述符均要為類屬性,當(dāng)描述符作為實(shí)例屬性出現(xiàn)時(shí),不會(huì)自動(dòng)調(diào)用描述符方法。
class Foo(object): def __init__(self): self.x = 1 self.y = 2 def __getattribute__(self,keys = None):#這樣優(yōu)先級(jí)鏈,描述符什么的就都沒用了。。。。 return 'test' f = Foo() print f.x,f.y,f.z #test,test,test,優(yōu)先級(jí)鏈?zhǔn)裁吹亩紱]用啦= =
__getattribute__()的Python實(shí)現(xiàn)大致如下,
def __getattribute__(self, key): "Emulate type_getattro() in Objects/typeobject.c" v = object.__getattribute__(self, key) if hasattr(v, '__get__'):#如果v定義了__get__方法的話,優(yōu)先調(diào)用v.__get__ return v.__get__(None, self) return v
調(diào)用描述符
描述符調(diào)用的細(xì)節(jié)取決于obj是一個(gè)對(duì)象還是一個(gè)類。不管是哪種,描述符只對(duì)新式對(duì)象和新式類起作用。繼承了 object 的類是新式類。
描述符也是一個(gè)對(duì)象,可以通過方法名直接調(diào)用描述符方法,如描述符d,d.__get__(object)。另外,在訪問描述符時(shí)會(huì)自動(dòng)調(diào)用相應(yīng)的描述符方法(只有為類屬性時(shí)才會(huì)自動(dòng)調(diào)用)。描述符的自動(dòng)調(diào)用機(jī)制基于__getattribute__() , __getattribute__()確保了descriptor的機(jī)制,所以,如果重寫了__getattribute__, 就可以消除descriptor機(jī)制。
對(duì)于對(duì)象來說,obj.__getattribute__()會(huì)將b.x轉(zhuǎn)化為 type(b).__dict__['x'].__get__(b,type(b)) ,按照下述順序搜索屬性:
類屬性中的數(shù)據(jù)描述符 > 實(shí)例變量 > 類屬性的非數(shù)據(jù)描述符 >
__getattr__()。
對(duì)于類來說,class.__getattribute__()會(huì)將B.x轉(zhuǎn)化為B.__dict__['x'].__get__(None,B) 。
記住以下幾點(diǎn):
1.描述符的調(diào)用基于__getattribute__();
2.重寫__getattribute__()會(huì)阻止描述符的正常調(diào)用;
3.object.__getattribute__()和type.__getattribute__()會(huì)調(diào)用不同的__get__();
4.數(shù)據(jù)描述符一直覆蓋實(shí)例屬性,即同時(shí)存在同名的數(shù)據(jù)描述符和實(shí)例屬性,優(yōu)先調(diào)用數(shù)據(jù)描述符
5.非數(shù)據(jù)描述符一直被實(shí)例屬性覆蓋,即同時(shí)存在同名的非數(shù)據(jù)描述符和實(shí)例屬性,優(yōu)先調(diào)用實(shí)例屬性;
描述符的機(jī)制在 object, type, 和 super 的 __getattribute__()方法中實(shí)現(xiàn)。由 object 派生出的類自動(dòng)繼承這個(gè)機(jī)制,或者它們有個(gè)有類似機(jī)制的元類。如果想讓描述符失效的話,可以重寫 __getattribute__() 。
class descriptor(object):
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print 'get', self.name,
return self.val
def __set__(self, obj, val):
print 'set', self.name,val
self.val = val
class Foo(object):
x = descriptor(10, 'var "x"')
y = 5
m = Foo()
'''訪問m.x的三種方法'''
print m.x #get var "x",10
print type(m).__dict__['x'].__get__(m,type(m)) #m.x會(huì)轉(zhuǎn)化為這種形式,等價(jià)于m.x
print m.__getattribute__('x') #等價(jià)于m.x,因?yàn)閤定義了__get__方法,調(diào)用x的__get__方法,上面已經(jīng)給出了__getattribute__的實(shí)現(xiàn)原理
'''設(shè)置m.x的值'''
m.x = 20 #set var "x" 20
type(m).__dict__['x'].__set__(m,20) #m.x = 20會(huì)轉(zhuǎn)化為此種形式,等價(jià)于m.x = 20
print m.x #get var "x",20
print m.y #5
#print type(m).__dict__['y'].__get__(m,type(m)) #error,AttributeError: 'int' object has no attribute '__get__'
print m.__getattribute__('y') #5,等價(jià)于m.y
描述符的陷阱
描述符應(yīng)放在類層次上,即作為類屬性(class level)
說了N多次了,要謹(jǐn)記…
class Foo(object): y = descriptor(0) def __init__(self): self.x = descriptor(1) #實(shí)例屬性的描述符是不會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的描述符方法的... b = Foo() print "X is %s, Y is %s" % (b.x, b.y)#X is <__main__.descriptor object at 0x10432c250>, Y is 0 print "Y is %s"%b.y.__get__(b) #需要自己調(diào)用__get__方法,解釋器不會(huì)自己調(diào)用的
從上述代碼可知,
- 訪問類層次上的描述符會(huì)自動(dòng)調(diào)用相應(yīng)的描述符方法;
- 訪問實(shí)例層次上的描述符只會(huì)返回描述符對(duì)象自身,并不會(huì)調(diào)用相應(yīng)的描述符方法;
(實(shí)例屬性的描述符不會(huì)自動(dòng)調(diào)用描述符方法,這么做肯定是有原因的吧…有知道的大神求指導(dǎo)…)
**描述符是所有實(shí)例共享的,讓不同實(shí)例保存的值互不影響
class descriptor(object): def __init__(self, default): self.value = default def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = value class Foo(object): bar = descriptor(5)
bar是類屬性,所有Foo的實(shí)例共享bar。
f = Foo() g = Foo() print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 5 g.bar is 5 f.bar = 10 #調(diào)用__set__函數(shù) print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 10 g.bar is 10
當(dāng)實(shí)例修改了描述符以后,會(huì)影響到其他實(shí)例,有沒有一種方法可以讓實(shí)例之間互不影響呢?
數(shù)據(jù)字典法
在descriptor中使用數(shù)據(jù)字典,由__get__和__set__的第一個(gè)參數(shù)來確定是哪個(gè)實(shí)例,使用實(shí)例作為字典的key,為每一個(gè)實(shí)例單獨(dú)保存一份數(shù)據(jù),修改代碼如下…
from weakref import WeakKeyDictionary class descriptor(object): def __init__(self, default): self.default = default self.data = WeakKeyDictionary() def __get__(self, instance, owner):# instance = x,owner = type(x) # we get here when someone calls x.d, and d is a descriptor instance return self.data.get(instance, self.default) def __set__(self, instance, value): # we get here when someone calls x.d = val, and d is a descriptor instance self.data[instance] = value class Foo(object): bar = descriptor(5) f = Foo() g = Foo() print "f.bar is %s g.bar is %s" % (f.bar, g.bar) #f.bar is 5 g.bar is 5 print "Setting f.bar to 10" f.bar = 10 print "f.bar is %s\ng.bar is %s" % (f.bar, g.bar) ##f.bar is 10 g.bar is 5
上述方法雖然可行,但是存在缺陷。
descriptor使用了一個(gè)字典來保存不同實(shí)例的數(shù)據(jù)。一般來說是不會(huì)出現(xiàn)問題,但是如果實(shí)例為不可哈希對(duì)象,如list,上述方法就會(huì)出現(xiàn)問題,因?yàn)椴豢晒?duì)象不能作為鍵值。
標(biāo)簽法
說白了就是給實(shí)例增加一個(gè)與描述符同名的實(shí)例屬性,利用該實(shí)例屬性來保存該實(shí)例描述符的值,描述符相當(dāng)于一個(gè)中間操作,描述符的__get__()返回實(shí)例屬性,__set__也是對(duì)實(shí)例屬性操作。
此方法的實(shí)現(xiàn)原理: 數(shù)據(jù)描述符的訪問優(yōu)先級(jí)比實(shí)例屬性高…
還是見圖和代碼吧,代碼最直觀…

class descriptor(object):
def __init__(self, label):#label為給實(shí)例增加的實(shí)例屬性名
self.label = label
def __get__(self, instance, owner):
#dict.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.
return instance.__dict__.get(self.label) #獲取與描述符同名的實(shí)例屬性的值
def __set__(self, instance, value):
#注意這里,要這么寫,不能寫instance.x = val這種形式,這樣會(huì)形成自身的循環(huán)調(diào)用
instance.__dict__[self.label] = value #修改與描述符同名的實(shí)例屬性的值
class Foo(list):
x = descriptor('x') #注意這個(gè)初始化值為要給實(shí)例增加的實(shí)例屬性名,要和描述符對(duì)象同名。
y = descriptor('y')
f1 = Foo()
f2 = Foo()
print f1.__dict__ #{}
print f1.x,f2.x,f1.y,f2.y#None None None None,此時(shí)尚未增加實(shí)例屬性,需要調(diào)用__set__方法建立一個(gè)與描述符同名的實(shí)例屬性
#print Foo.__dict__
f1.x = 1
f1.y = 2
f2.x = 3
f2.y = 4
print f1.__dict__ #{'y': 2, 'x': 1} #增加了的實(shí)例屬性
print f1.x,f1.y,f2.x,f2.y #1 2 3 4
因?yàn)橹挥姓{(diào)用了__set__函數(shù)才會(huì)建立一個(gè)與描述符同名的實(shí)例屬性,所以可以在__init__()函數(shù)中對(duì)描述符賦值。
class Foo(list):
x = descriptor('x')
y = descriptor('y')
def __init__(self):
self.x = 1 #調(diào)用的是描述符的__set__方法,與描述符同名的實(shí)例屬性增加完畢....
self.y = 2
f = Foo()
print f.x,f.y
注意事項(xiàng):
給描述符添加標(biāo)簽時(shí),初始化值要和描述符的變量名相同,比如name = descriptor(‘name'),因?yàn)檫@個(gè)初始化值是給實(shí)例增加的實(shí)例屬性名,必須要和描述符對(duì)象同名。
下面為錯(cuò)誤示范,初始化值和描述符不同名的情況。
class descriptor(object):
def __init__(self, label):
self.label = label
def __get__(self, instance, owner):
return instance.__dict__.get(self.label)
def __set__(self, instance, value):
if not isinstance(value,int):
raise ValueError('must be int')
instance.__dict__[self.label] = value
class Foo(object):
x = descriptor('y') #應(yīng)該改為Descriptor('x'),與實(shí)例同名
def __init__(self,val = 0):
self.x = val
f = Foo()
print f.x #0
f.y = 'a' #繞過了描述符的__set__方法...未進(jìn)行類型檢查,此時(shí)x為非法值啊,是不是很坑...
print f.x #a
潛在坑…正常使用時(shí)也會(huì)帶來坑。
class Foo(object):
x = descriptor('x')
def __init__(self,val = 0):
self.x = val
f = Foo()
f.x = 'a'#ValueError: must be int
好像沒毛病啊…接著往下看。
f.__dict__['x'] = 'a' print f.x #a,還是繞過了__set__方法,未進(jìn)行類型檢查,還是非法值啊...哈哈- -
小結(jié)
查了很多資料,標(biāo)簽法用的較多,但是標(biāo)簽法也有一定的缺陷,目前沒有找到更好的方法解決上述問題,如有更好的方法,求指導(dǎo),謝謝…
描述符的應(yīng)用…
描述符也是類,可以為其增加方法。比如增加回調(diào)方法,描述符是一個(gè)用來回調(diào)的很好的手段。比如想要一個(gè)類的某個(gè)狀態(tài)發(fā)生變化就立刻通知我們,可以自定義回調(diào)函數(shù)用來響應(yīng)類中的狀態(tài)變化。如以下代碼,
from weakref import WeakKeyDictionary
class CallbackProperty(object):
def __init__(self, default=None):
self.data = WeakKeyDictionary()
self.default = default
self.callbacks = WeakKeyDictionary()
def __get__(self, instance, owner):
if instance is None:
return self
return self.data.get(instance, self.default)
def __set__(self, instance, value):#每次改變值的時(shí)候都會(huì)調(diào)用low_balance_warning函數(shù)
for callback in self.callbacks.get(instance, []):
# alert callback function of new value
callback(value)
self.data[instance] = value
def add_callback(self, instance, callback):
"""Add a new function to call everytime the descriptor within instance updates"""
if instance not in self.callbacks:
self.callbacks[instance] = [] #實(shí)例->[方法,]
self.callbacks[instance].append(callback)
class BankAccount(object):
balance = CallbackProperty(0)
def low_balance_warning(value):
if value < 100:
print "You are now poor"
else:
print 'You are now rich!!!'
def check(value):
print 'You have %s money, Good Luck!!!'%value
ba = BankAccount()
BankAccount.balance.add_callback(ba, low_balance_warning)
BankAccount.balance.add_callback(ba, check)
ba.balance = 5000 # You are now rich!!! You have 5000 money, Good Luck!!!
print "Balance is %s" % ba.balance # Balance is 5000
ba.balance = 99 # You are now poor You have 99 money, Good Luck!!!
print "Balance is %s" % ba.balance # Balance is 99
有木有感覺很厲害…__set__()方法感覺就像一個(gè)監(jiān)督人員,監(jiān)視屬性的一舉一動(dòng)。
描述符還有其他用處,如格式檢查,類型檢查,設(shè)置只讀變量等。設(shè)置一個(gè)只讀變量的話,只要不讓變量再賦值就好了,即調(diào)用__set__()函數(shù)時(shí)觸發(fā)異常即可,這也是問題2的答案。
class descriptor(object):
def __init__(self,val):
self.val = val
def __get__(self, obj,type = None):
return self.val
def __set__(self, obj, value):
raise Exception('read only')
class Foo(object):
d = descriptor(1)
d = Foo()
print d.d #1
d.d = 2 #觸發(fā)異常,read only
參考網(wǎng)址
1.https://docs.python.org/2/howto/descriptor.html#definition-and-introduction
2.https://hg.python.org/cpython/file/2.7/Objects/object.c
3.http://svn.python.org/view/python/trunk/Objects/classobject.c?view=markup
4.http://www.geekfan.net/7862/
5.http://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html
6.//www.dbjr.com.cn/article/62987.htm
7.//www.dbjr.com.cn/article/97741.htm
8.http://www.tuicool.com/articles/yYJbqun
到此這篇關(guān)于Python descriptor(描述符)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Python descriptor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何從Python 程序中運(yùn)行 PowerShell 腳本
Windows PowerShell 由數(shù)十個(gè)內(nèi)置 cmdlet 組成,它們提供了豐富的功能集,本文將重點(diǎn)討論從 Python 代碼執(zhí)行 PowerShell 邏輯,感興趣的朋友跟隨小編一起看看吧2023-10-10
由Python運(yùn)算π的值深入Python中科學(xué)計(jì)算的實(shí)現(xiàn)
這篇文章主要介紹了由Python運(yùn)算π的值深入Python中科學(xué)計(jì)算的實(shí)現(xiàn),由簡(jiǎn)單的計(jì)算發(fā)散出各種算法的講解,需要的朋友可以參考下2015-04-04
Python-Tkinter Text輸入內(nèi)容在界面顯示的實(shí)例
今天小編就為大家分享一篇Python-Tkinter Text輸入內(nèi)容在界面顯示的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07
使用70行Python代碼實(shí)現(xiàn)一個(gè)遞歸下降解析器的教程
這篇文章主要介紹了使用70行Python代碼實(shí)現(xiàn)一個(gè)遞歸下降解析器的教程,文章分步講解最后整合出代碼,需要的朋友可以參考下2015-04-04
機(jī)器學(xué)習(xí)之KNN算法原理及Python實(shí)現(xiàn)方法詳解
這篇文章主要介紹了機(jī)器學(xué)習(xí)之KNN算法原理及Python實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了機(jī)器學(xué)習(xí)KNN算法原理以及Python相關(guān)實(shí)現(xiàn)步驟、操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-07-07

