程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Python >> python描述符、property、函數(類)裝飾器實例解析

python描述符、property、函數(類)裝飾器實例解析

編輯:Python

 

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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved