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 '''