import sys
'''
當使用實例對象訪問屬性時,都會調用__getattribute__內建函數
__getattribute__查找屬性的優先級
1、類屬性
2、數據描述符
3、實例屬性
4、非數據描述符
5、__getattr__()
#實例.屬性
c.x ==>type(x).__dict__['x'].__get__(x,type(x))
#類.屬性
C.x ==>X.__dict__['x'].__get__(None,C)
__getattribute__偽代碼:
__getattribute__(property) logic:
#先在類(包括父類、祖先類)的__dict__屬性中查找描述符
descripter = find first descripter in class and bases's dict(property)
if descripter:#如果找到屬性並且是數據描述符,就直接調用該數據描述符的__get__方法並將結果返回
return descripter.__get__(instance, instance.__class__)
else:#如果沒有找到或者不是數據描述符,就去實例的__dict__屬性中查找屬性,如果找到了就直接返回這個屬性
if value in instance.__dict__
return value
#程序執行到這裡,說明沒有數據描述符和實例屬性,則在類(父類、祖先類)的__dict__屬性中查找非數據描述符
value = find first value in class and bases's dict(property)
if value is a function:#如果找到了並且這個屬性是一個函數,就返回綁定後的函數
return bounded function(value)
else:#否則就直接返回這個屬性
return value
#程序執行到這裡說明沒有找到該屬性,引發異常,__getattr__函數會被調用
raise AttributeNotFundedException
__setattr__偽代碼:
__setattr__(property, value)logic:
#先在類(包括父類、祖先類)的__dict__屬性中查找描述符
descripter = find first descripter in class and bases's dict(property)
if descripter:#如果找到了且是數據描述符,就調用描述符的__set__方法
descripter.__set__(instance, value)
else:#否則就是給實例屬性賦值
instance.__dict__[property] = value
'''
#帶參數函數裝飾器
def log(header,footer):#相當於在無參裝飾器外套一層參數
def log_to_return(fun):#這裡接受被裝飾的函數
def return_fun(*args,**kargs):
print(header)
fun(*args,**kargs)
print(footer)
return return_fun
return log_to_return
#帶參數類型裝飾器
def flyable(message):
def flyable_to_return(cls):
def fly(self):
print(message)
cls.fly = fly #類屬性也可以動態修改
return cls
return flyable_to_return
#say(meaasge) ==> log(parms)(say)(message)
@log('日志輸出開始','結束日志輸出')
def say(message):
print(message)
#定義一個非數據描述符
class myStaticObject(object):
def __init__(self,fun):
self.fun = fun
def __get__(self,instance,owner):
print('call myStaticObject __get__')
return self.fun
#無參的函數裝飾器,返回的是非數據描述符對象
def my_static_method(fun):
return myStaticObject(fun)
#定義一個非數據描述符
class myClassObject(object):
def __init__(self,fun):
self.fun = fun
def __get__(self,instance,owner):
print('call myClassObject __get__')
def class_method(*args,**kargs):
return self.fun(owner,*args,**kargs)
return class_method
#無參的函數裝飾器,返回的是非數據描述符對象
def my_class_method(fun):
return myClassObject(fun)
#非數據描述符
class des1(object):
def __init__(self,name=None):
self.__name = name
def __get__(self,obj,typ=None):
print('call des1.__get__')
return self.__name
#數據描述符
class des2(object):
def __init__(self,name=None):
self.__name = name
def __get__(self,obj,typ=None):
print('call des2.__get__')
return self.__name
def __set__(self,obj,val):
print('call des2.__set__,val is %s' % (val))
self.__name = val
#測試類
@flyable("這是一個測試類")
class test(object):
def __init__(self,name='test',age=0,sex='man'):
self.__name = name
self.__age = age
self.__sex = sex
#---------------------覆蓋默認的內建方法
def __getattribute__(self, name):
print("start call __getattribute__")
return super(test, self).__getattribute__(name)
def __setattr__(self, name, value):
print("before __setattr__")
super(test, self).__setattr__(name, value)
print("after __setattr__")
def __getattr__(self,attr):
print("start call __getattr__")
return attr
#此處可以使用getattr()內建函數對包裝對象進行授權
def __str__(self):
return str('name is %s,age is %d,sex is %s' % (self.__name,self.__age,self.__sex))
__repr__ = __str__
#-----------------------
d1 = des1('chenyang') #非數據描述符,可以被實例屬性覆蓋
d2 = des2('pengmingyao') #數據描述符,不能被實例屬性覆蓋
def d3(self): #普通函數,為了驗證函數(包括函數、靜態/類方法)都是非數據描述符,可悲實例屬性覆蓋
print('i am a function')
#------------------------
def get_name(self):
print('call test.get_name')
return self.__name
def set_name(self,val):
print('call test.set_name')
self.__name = val
name_proxy = property(get_name,set_name)#數據描述符,不能被實例屬性覆蓋,property本身就是一個描述符類
def get_age(self):
print('call test.get_age')
return self.__age
age_proxy = property(get_age) #非數據描述符,但是也不能被實例屬性覆蓋
#----------------------
@property
def sex_proxy(self):
print("call get sex")
return self.__sex
@sex_proxy.setter #如果沒有setter裝飾,那麼sex_proxy也是只讀的,實例屬性也無法覆蓋,同property
def sex_proxy(self,val):
print("call set sex")
self.__sex = val
#---------------------
@my_static_method #相當於my_static_fun = my_static_method(my_static_fun) 就是非數據描述符
def my_static_fun():
print('my_static_fun')
@my_class_method
def my_class_fun(cls):
print('my_class_fun')
#end
if __name__ == "__main__":
say("函數裝飾器測試")
'''
日志輸出開始
函數裝飾器測試
結束日志輸出
'''
t=test( ) #創建測試類的實例對象
'''
before __setattr__
after __setattr__
before __setattr__
after __setattr__
before __setattr__
after __setattr__
'''
print(str(t)) #驗證__str__內建函數
'''
start call __getattribute__
start call __getattribute__
start call __getattribute__
name is test,age is 0,sex is man
'''
print(repr(t))#驗證__repr__內建函數
'''
start call __getattribute__
start call __getattribute__
start call __getattribute__
name is test,age is 0,sex is man
'''
t.fly() #驗證類裝飾器
'''
start call __getattribute__
這是一個測試類
'''
t.my_static_fun()#驗證自定義靜態方法
'''
start call __getattribute__
call myStaticObject __get__
my_static_fun
'''
t.my_class_fun()#驗證自定義類方法
'''
start call __getattribute__
call myClassObject __get__
my_class_fun
'''
#以下為屬性獲取
t.d1
'''
start call __getattribute__
call des1.__get__
'''
t.d2
'''
start call __getattribute__
call des2.__get__
'''
t.d3()
'''
start call __getattribute__
i am a function
'''
t.name_proxy
'''
start call __getattribute__
call test.get_name
start call __getattribute__
'''
t.age_proxy
'''
start call __getattribute__
call test.get_age
start call __getattribute__
'''
t.sex_proxy
'''
start call __getattribute__
call get sex
start call __getattribute__
'''
t.xyz #測試訪問不存在的屬性,會調用__getattr__
'''
start call __getattribute__
start call __getattr__
'''
#測試屬性寫
t.d1 = 3 #由於類屬性d1是非數據描述符,因此這裡將動態產生實例屬性d1
'''
before __setattr__
after __setattr__
'''
t.d1 #由於實例屬性的優先級比非數據描述符優先級高,因此此處訪問的是實例屬性
'''
start call __getattribute__
'''
t.d2 = 'modefied'
'''
before __setattr__
call des2.__set__,val is modefied
after __setattr__
'''
t.d2
'''
start call __getattribute__
call des2.__get__
'''
t.d3 = 'not a function'
'''
before __setattr__
after __setattr__
'''
t.d3
'''
start call __getattribute__
'''
t.name_proxy = 'modified'
'''
before __setattr__
call test.set_name
before __setattr__
after __setattr__
after __setattr__
'''
t.sex_proxy = 'women'
'''
before __setattr__
call set sex
before __setattr__
after __setattr__
after __setattr__
'''
t.age_proxy = 3
'''
before __setattr__
Traceback (most recent call last):
File "test.py", line 191, in <module>
t.age_proxy = 3
File "test.py", line 121, in __setattr__
super(test, self).__setattr__(name, value)
AttributeError: can't set attribute
'''