半天鐘的博客元編程相關(guān)博文的目錄及鏈接這篇博文是元編程系列博文中的其中一篇、這個(gè)系列中其他博文的目錄和連接見下:使用python特性管理實(shí)例屬性淺析python屬性描述符(上)淺析python屬性描述符(下)python導(dǎo)入時(shí)與運(yùn)行時(shí)python元編程之動態(tài)屬性python元編程之類元編程Review在上一篇博文中、我們使用python特性(property)管理了實(shí)例屬性,最大的好處是:在使用property裝飾器后," />

黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

淺析 python 屬性描述符(上)

系統(tǒng) 2058 0

轉(zhuǎn)載自我自己的 github 博客 ——> 半天鐘的博客

元編程相關(guān)博文的目錄及鏈接

這篇博文是元編程系列博文中的其中一篇、這個(gè)系列中其他博文的目錄和連接見下:

  1. 使用 python 特性管理實(shí)例屬性
  2. 淺析 python 屬性描述符(上)
  3. 淺析 python 屬性描述符(下)
  4. python 導(dǎo)入時(shí)與運(yùn)行時(shí)
  5. python 元編程之動態(tài)屬性
  6. python 元編程之類元編程

Review

在上一篇博文中、我們使用 python 特性(property)管理了實(shí)例屬性,最大的好處是:在使用 property 裝飾器后,我們 能夠在通過使用 " . " 這種方便的方式(obj.attr)來訪問實(shí)例屬性的同時(shí),為其設(shè)置存儲規(guī)則。

并且,因?yàn)樘幚泶鎯Φ暮瘮?shù)都有 @property 或者 @特性名.setter 這種 明顯的裝飾器標(biāo)志 ,我們 可以很容易的找到處理業(yè)務(wù)邏輯的核心函數(shù)。

然而、 當(dāng)大量的屬性都需要相同的存取邏輯做控制時(shí) ,例如:水果類的 weight 和 price 的值均不能小于零。 單純的使用 property 依然無法避免代碼的重復(fù)。

當(dāng)重復(fù)為 weight 和 price 編寫幾乎相同的代碼時(shí), 重構(gòu)代碼的時(shí)機(jī)到了!!!

運(yùn)用和 property 同宗 的屬性描述符可以很好的避免代碼段的重復(fù)。

博文的編寫思路

首先、我會直接亮出使用屬性描述符重構(gòu)的代碼(基于上一篇博文的 Fruits 類), 用以給你屬性描述符,以及其如何減少重復(fù)代碼的直觀印象。

然后、我會仔細(xì)的說明 實(shí)現(xiàn)屬性描述符需要注意的細(xì)節(jié) 屬性描述符的性質(zhì) 及其如何與托管類實(shí)例交互。

接著、我會說明為什么 python 特性也是一種屬性描述符

最后、我會舉例說明屬性描述符比之 python 特性和優(yōu)勢在哪里

使用屬性描述符進(jìn)行高效管理

下面這段代碼是基于上一篇博文的 Fruit 類進(jìn)行的重構(gòu):

            
              
                class
              
              
                Quantity
              
              
                :
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               attrname
              
                )
              
              
                :
              
              
        self
              
                .
              
              attrname 
              
                =
              
               attrname

    
              
                def
              
              
                __get__
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               owner
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "get:"
              
              
                +
              
              
                str
              
              
                (
              
              instance
              
                .
              
              description
              
                )
              
              
                +
              
              
                "的"
              
              
                +
              
              
                str
              
              
                (
              
              self
              
                .
              
              attrname
              
                )
              
              
                )
              
              
                return
              
               instance
              
                .
              
              __dict__
              
                [
              
              self
              
                .
              
              attrname
              
                ]
              
              
                def
              
              
                __set__
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "set:"
              
              
                +
              
              
                str
              
              
                (
              
              instance
              
                .
              
              description
              
                )
              
              
                +
              
              
                "的"
              
              
                +
              
              
                str
              
              
                (
              
              self
              
                .
              
              attrname
              
                )
              
              
                )
              
              
                if
              
               value 
              
                >
              
              
                0
              
              
                :
              
              
            instance
              
                .
              
              __dict__
              
                [
              
              self
              
                .
              
              attrname
              
                ]
              
              
                =
              
               value
        
              
                else
              
              
                :
              
              
                raise
              
               ValueError
              
                (
              
              
                "想干嘛呢?"
              
              
                )
              
              
                class
              
              
                Fruits
              
              
                :
              
              

    weight 
              
                =
              
               Quantity
              
                (
              
              
                "weight"
              
              
                )
              
              
    price 
              
                =
              
               Quantity
              
                (
              
              
                "price"
              
              
                )
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               price
              
                ,
              
               weight
              
                ,
              
               description
              
                )
              
              
                :
              
              
                # 水果的描述
              
              
        self
              
                .
              
              description 
              
                =
              
               description
        
              
                # 水果的價(jià)格
              
              
        self
              
                .
              
              price 
              
                =
              
               price
        
              
                # 水果的重量
              
              
        self
              
                .
              
              weight 
              
                =
              
               weight

    
              
                def
              
              
                subtotal
              
              
                (
              
              self
              
                )
              
              
                :
              
              
                # 小記
              
              
                return
              
               self
              
                .
              
              price 
              
                *
              
               self
              
                .
              
              weight
        

              
                >>
              
              
                >
              
               apple 
              
                =
              
               Fruits
              
                (
              
              
                10
              
              
                ,
              
              
                2
              
              
                ,
              
              
                "apple"
              
              
                )
              
              
                # 1
              
              
                set
              
              
                :
              
              apple的price

              
                set
              
              
                :
              
              apple的weight

              
                >>
              
              
                >
              
               pear 
              
                =
              
               Fruits
              
                (
              
              
                11
              
              
                ,
              
              
                3
              
              
                ,
              
              
                "pear"
              
              
                )
              
              
                set
              
              
                :
              
              pear的price

              
                set
              
              
                :
              
              pear的weight

              
                >>
              
              
                >
              
              
                vars
              
              
                (
              
              apple
              
                )
              
              
                # 2
              
              
                {
              
              
                'description'
              
              
                :
              
              
                'apple'
              
              
                ,
              
              
                'price'
              
              
                :
              
              
                10
              
              
                ,
              
              
                'weight'
              
              
                :
              
              
                2
              
              
                }
              
              
                >>
              
              
                >
              
              
                vars
              
              
                (
              
              pear
              
                )
              
              
                {
              
              
                'description'
              
              
                :
              
              
                'pear'
              
              
                ,
              
              
                'price'
              
              
                :
              
              
                11
              
              
                ,
              
              
                'weight'
              
              
                :
              
              
                3
              
              
                }
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              weight 
              
                # 3
              
              
get
              
                :
              
              apple的weight

              
                2
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              price 
get
              
                :
              
              apple的price

              
                10
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              weight 
              
                =
              
              
                10
              
              
                # 4
              
              
                set
              
              
                :
              
              apple的weight

              
                >>
              
              
                >
              
               apple
              
                .
              
              weight 
              
                # 5
              
              
get
              
                :
              
              apple的weight

              
                10
              
              
                >>
              
              
                >
              
              
                vars
              
              
                (
              
              pear
              
                )
              
              
                # 6
              
              
                {
              
              
                'description'
              
              
                :
              
              
                'pear'
              
              
                ,
              
              
                'price'
              
              
                :
              
              
                11
              
              
                ,
              
              
                'weight'
              
              
                :
              
              
                3
              
              
                }
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              price 
              
                =
              
              
                -
              
              
                1
              
              
                # 7
              
              
Traceback 
              
                (
              
              most recent call last
              
                )
              
              
                :
              
              
                .
              
              
                .
              
              
                .
              
              
ValueError
              
                :
              
               想干嘛呢?

            
          

上例使用屬性描述符類 Quantity 類對 Fruits 類進(jìn)行了重構(gòu),使用了 Quantity 實(shí)例作為 Fruits 類的 weight 和 price 的類屬性值。

可以看到、 重構(gòu)后的 Fruits 類實(shí)例有如下行為

  1. 初始化 Fruits 類實(shí)例時(shí), 會調(diào)用描述符實(shí)例的 __set__ magic 方法。
  2. 查看實(shí)例 apple 的全部屬性、發(fā)現(xiàn)其 有 price 和 weight 實(shí)例屬性 。這是由于 __set__ magic 方法使用 instance.__dict__[self.attrname] = value 語句為實(shí)例屬性設(shè)了值
  3. 可以通過 apple.weight 訪問實(shí)例屬性 ,但是其是通過描述符實(shí)例的 __get__ magic 方法來訪問的。
  4. 可以通過 apple.weight = 10 這種方式為實(shí)例屬性設(shè)置值 ,但是其實(shí)通過描述符實(shí)例的 __set__ magic 方法來訪問的。
  5. 能夠訪問到剛剛為 apple 實(shí)例設(shè)置的值。
  6. 查看 pear 實(shí)例的全部屬性、發(fā)現(xiàn) 剛才對 apple 實(shí)例的所有操作對 pear 實(shí)例毫無影響。
  7. 若設(shè)置 value 為負(fù)值,那么會拋出異常。

如果你不明白這些行為的原理,沒關(guān)系,我會在下一小節(jié)解釋屬性描述符的原理,現(xiàn)在你只需要知道,重構(gòu)后的代碼有著這樣的行為。

重構(gòu)后的好處

好了,在使用了 Quantity 屬性描述重構(gòu)了 Fruits 類之后、 我們依然可以使用 " . " 方便的訪問實(shí)例屬性,并同時(shí)做存儲邏輯的驗(yàn)證。

而且、**我僅用了 30 行代碼就實(shí)現(xiàn)了 weight 和 price 兩個(gè)實(shí)例屬性的管理。**要知道、上一篇博文中,僅實(shí)現(xiàn)了 weight 屬性的管理就寫了 27 行代碼。

不僅是代碼量減少了,如果水果店老板想為所有水果都增加一個(gè)折扣屬性(discount)、其也不能為負(fù)值。那么我們只需要在 Fruit 類中增加一行代碼 discount = Quantity("discount") 這大大減少了重復(fù)代碼,提高了代碼的可重用性。

屬性描述符原理

為了說明白屬性描述符的原理,我將先說明一些專有名詞。

專有名詞

描述符類

  • 實(shí)現(xiàn)了描述符協(xié)議的類 、比如上例中的 Quantity 類、它實(shí)現(xiàn)了描述符類的一些協(xié)議( __get__ __set__ )。

實(shí)現(xiàn)了 __get__ __set__ __delete__ 方法的類是描述符,只要實(shí)現(xiàn)了其中一個(gè)就是。

托管類

  • 將描述符實(shí)例作為類屬性的類 ,比如上例中的 Fruits 類,他有 weight、price 兩個(gè)類屬性,且都被賦予了描述符類的實(shí)例。

描述符實(shí)例

  • 描述符類的實(shí)例 、比如上例中 Fruits 類中就用 Quantity("weight") 創(chuàng)建了一個(gè)描述符實(shí)例, 通常來講,描述符類的實(shí)例會被賦給托管類的類屬性。

托管實(shí)例

  • 托管類的實(shí)例 、比如上例中的 apple 、 pear。

托管屬性

  • 托管類中由描述符實(shí)例處理的公開屬性 、比如上例中 Fruits 類的 類屬性 weight、price

存儲屬性

  • 可以粗略的理解為、托管實(shí)例的屬性 、在上例中使用 vars(apple) 得到的結(jié)果中 price 和 weight 實(shí)例屬性 就是存儲屬性,它們實(shí)際 存儲著 * 實(shí)例的 * 屬性值

你可能不理解存儲屬性和托管屬性的區(qū)別、因?yàn)樵谏侠? 托管屬性與存儲屬性同名
那么,你只需要記住: 托管屬性是類(Fruits)屬性、存儲屬性是實(shí)例(apple)的屬性。

描述符類與托管類的關(guān)系

首先,描述符類與托管類都是類,可以將他們想象成類的工廠、下面兩張圖很好的展示了他們之間的關(guān)系:

以下兩張圖修改自《流暢的 python》 第 20 章

淺析 python 屬性描述符(上)_第1張圖片

如上圖、Quantity 作為描述符實(shí)例的工廠、 產(chǎn)出了兩個(gè)實(shí)例并綁定至 Fruits 類的類屬性 weight、price

Fruit 作為托管類實(shí)例的工廠、可以產(chǎn)出多個(gè)實(shí)例、 每一個(gè)實(shí)例都有兩個(gè)存儲屬性 weight、price

淺析 python 屬性描述符(上)_第2張圖片

上圖將描述符的兩個(gè)實(shí)例抽象成了兩個(gè)小機(jī)器人、手上拿著一個(gè)放大鏡和一個(gè)手抓, 放大鏡用于獲取托管類實(shí)例的值( __get__ )、手抓用于設(shè)置托管類實(shí)例的值( __set__ )

值得注意的是、Fruits 工廠不管生產(chǎn)多少實(shí)例, 都只能擁有兩個(gè)描述符小機(jī)器人 ,因?yàn)樗鼈兪穷悓傩浴?

描述符實(shí)例如何工作

如果你看過上一篇博文、你可能還記得上一篇博文中的特性的工作流程圖。實(shí)際上特性就是一種屬性描述符、所以在這里 Quantity 屬性描述符的工作流程與特性幾乎一致 ,例如: apple.weight = 10 語句的執(zhí)行過程如下面的流程圖:

淺析 python 屬性描述符(上)_第3張圖片

以上流程、對于 Quantity 這個(gè)類型的描述符而言, apple.weight 這樣的代碼的執(zhí)行流程與上圖幾乎沒有差別,無非是在搜索到有 weight 描述符實(shí)例時(shí),調(diào)用 __get__ magic 方法;在搜索到有 weight 實(shí)例屬性時(shí)獲取該屬性的值;都搜索不到則拋出異常。

property 是一種屬性描述符

為什么說 python 特性也是一種屬性描述符呢?讓我們看 python 2.2 之前是如何使用 property的:

property 類實(shí)現(xiàn)了完整的描述符協(xié)議

            
              
                def
              
              
                get_weight
              
              
                (
              
              instance
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "get weight"
              
              
                )
              
              
                return
              
               instance
              
                .
              
              __dict__
              
                [
              
              
                "weight"
              
              
                ]
              
              
                def
              
              
                set_weight
              
              
                (
              
              instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                print
              
              
                (
              
              
                "set weight"
              
              
                )
              
              
                if
              
               value 
              
                >
              
              
                0
              
              
                :
              
              
        instance
              
                .
              
              __dict__
              
                [
              
              
                "weight"
              
              
                ]
              
              
                =
              
               value
    
              
                else
              
              
                :
              
              
                raise
              
               ValueError
              
                (
              
              
                "想干嘛呢?"
              
              
                )
              
              
                class
              
              
                Fruits
              
              
                :
              
              

    weight 
              
                =
              
              
                property
              
              
                (
              
              get_weight
              
                ,
              
               set_weight
              
                )
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               price
              
                ,
              
               weight
              
                ,
              
               description
              
                )
              
              
                :
              
              
                # 水果的描述
              
              
        self
              
                .
              
              description 
              
                =
              
               description
        
              
                # 水果的價(jià)格
              
              
        self
              
                .
              
              price 
              
                =
              
               price
        
              
                # 水果的重量
              
              
        self
              
                .
              
              weight 
              
                =
              
               weight

    
              
                def
              
              
                subtotal
              
              
                (
              
              self
              
                )
              
              
                :
              
              
                # 小記
              
              
                return
              
               self
              
                .
              
              price 
              
                *
              
               self
              
                .
              
              weight


              
                >>
              
              
                >
              
               apple 
              
                =
              
               Fruits
              
                (
              
              
                10
              
              
                ,
              
              
                2
              
              
                ,
              
              
                "apple"
              
              
                )
              
              
                set
              
               weight

              
                >>
              
              
                >
              
              
                vars
              
              
                (
              
              apple
              
                )
              
              
                {
              
              
                'description'
              
              
                :
              
              
                'apple'
              
              
                ,
              
              
                'price'
              
              
                :
              
              
                10
              
              
                ,
              
              
                'weight'
              
              
                :
              
              
                2
              
              
                }
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              weight
get weight

              
                2
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              weight 
              
                =
              
              
                10
              
              
                set
              
               weight

              
                >>
              
              
                >
              
               apple
              
                .
              
              weight
get weight

              
                10
              
              
                >>
              
              
                >
              
               apple
              
                .
              
              weight 
              
                =
              
              
                -
              
              
                1
              
              
Traceback 
              
                (
              
              most recent call last
              
                )
              
              
                :
              
              
                .
              
              
                .
              
              
                .
              
              
ValueError
              
                :
              
               想干嘛呢?

            
          

你看出來了嗎?上例中的我們寫了一對 set/get 方法,并用他們生成了一個(gè) property 對象,賦予了 Fruits 類的 weight 類屬性。這和屬性描述符類 Quantity 有太多的相似之處。

實(shí)際上 property 類的構(gòu)造方法返回一個(gè)描述符實(shí)例,該實(shí)例的 __get__ magic方法即是get_weight、 __set__ magic 方法既是 set_weight。

甚至這樣做以后、 也能很好的管理 Fruits 實(shí)例的 weight 屬性 ,行為與使用 Quantity 一致。

肯定有人會說、既然這樣, 那我完全沒有必要使用 Quantity 了,直接寫一個(gè)特性工廠函數(shù)即可!!!
比如:

代碼來自《流暢的 python》第19章

            
              
                def
              
              
                quantity
              
              
                (
              
              storage_name
              
                )
              
              
                :
              
              
                def
              
              
                qty_getter
              
              
                (
              
              instance
              
                )
              
              
                :
              
              
                return
              
               instance
              
                .
              
              __dict__
              
                [
              
              storage_name
              
                ]
              
              
                def
              
              
                qty_setter
              
              
                (
              
              instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                if
              
               value 
              
                >
              
              
                0
              
              
                :
              
              
            instance
              
                .
              
              __dict__
              
                [
              
              storage_name
              
                ]
              
              
                =
              
               value  
        
              
                else
              
              
                :
              
              
                raise
              
               ValueError
              
                (
              
              
                'value must be > 0'
              
              
                )
              
              
                return
              
              
                property
              
              
                (
              
              qty_getter
              
                ,
              
               qty_setter
              
                )
              
            
          

這樣一來、 weight = Quantity("weight") 就可以轉(zhuǎn)變?yōu)? weight = quantity('weight') 。甚至比創(chuàng)建 Quantity 類的代碼還要短。

但是,對于描述符類來說、依舊有著得天獨(dú)厚的優(yōu)勢, 即面向?qū)ο蟮姆绞?

描述符類能夠繼承

現(xiàn)在,項(xiàng)目的產(chǎn)品經(jīng)理小姐姐提出了一個(gè)合理的需求 —— **Fruits 的描述不能為空!!**這很合理,因?yàn)槊枋鰹榭諘r(shí),顧客在系統(tǒng)中根本看不到自己買的是哪種水果。

輪到程序猿頭疼了,難道再增加一個(gè)描述符類,或者特性工廠函數(shù)嗎?如果這樣做了,那 小姐姐以后又提出一種新屬性的存取邏輯怎么辦?

我們注意到、不管是值不能小于零、還是描述不能為空, 二者的存取邏輯都在 set 方法上,對于 get 方法幾乎沒有邏輯驗(yàn)證。

那么我們?yōu)槭裁床粚懸粋€(gè) 描述符類 、其實(shí)現(xiàn)了通用的 __get__ 方法,再設(shè)置一個(gè)抽象的驗(yàn)證方法、新來的描述符類只需要繼承該抽象類,再覆蓋該驗(yàn)證方法即可。這樣能夠極大的節(jié)省代碼冗余。

這種思想通常被稱為模板方法設(shè)計(jì)模式

實(shí)現(xiàn)代碼如下:

            
              
                import
              
               abc



              
                class
              
              
                AutoStorage
              
              
                :
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               attrname
              
                )
              
              
                :
              
              
        self
              
                .
              
              attrname 
              
                =
              
               attrname

    
              
                def
              
              
                __get__
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               owner
              
                )
              
              
                :
              
              
                # __get__ 方法除了必要的判斷 instance 是否真實(shí)存在以外,操作與之前幾乎一致
              
              
                if
              
               instance 
              
                is
              
              
                None
              
              
                :
              
              
                return
              
               self
        
              
                else
              
              
                :
              
              
                return
              
               instance
              
                .
              
              __dict__
              
                [
              
              self
              
                .
              
              attrname
              
                ]
              
              
                def
              
              
                __set__
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                # 1
              
              
        instance
              
                .
              
              __dict__
              
                [
              
              self
              
                .
              
              attrname
              
                ]
              
              
                =
              
               value



              
                class
              
              
                Validated
              
              
                (
              
              abc
              
                .
              
              ABC
              
                ,
              
               AutoStorage
              
                )
              
              
                :
              
              
                # 2
              
              
                def
              
              
                __set__
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
        value 
              
                =
              
               self
              
                .
              
              validate
              
                (
              
              instance
              
                ,
              
               value
              
                )
              
              
                # 3
              
              
                super
              
              
                (
              
              
                )
              
              
                .
              
              __set__
              
                (
              
              instance
              
                ,
              
               value
              
                )
              
              
                # 4
              
              

    @abc
              
                .
              
              abstractmethod  
    
              
                def
              
              
                validate
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                # 5
              
              
                """返回經(jīng)過驗(yàn)證的值或拋出異常"""
              
              
                class
              
              
                Quantity
              
              
                (
              
              Validated
              
                )
              
              
                :
              
              
                """驗(yàn)證值是否大于等于零"""
              
              
                def
              
              
                validate
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                # 6
              
              
                if
              
               value 
              
                <
              
              
                0
              
              
                :
              
              
                raise
              
               ValueError
              
                (
              
              
                'value must be > 0'
              
              
                )
              
              
                return
              
               value



              
                class
              
              
                NonBlank
              
              
                (
              
              Validated
              
                )
              
              
                :
              
              
                """驗(yàn)證字符串是否不為空"""
              
              
                def
              
              
                validate
              
              
                (
              
              self
              
                ,
              
               instance
              
                ,
              
               value
              
                )
              
              
                :
              
              
                # 7
              
              
        value 
              
                =
              
               value
              
                .
              
              strip
              
                (
              
              
                )
              
              
                if
              
              
                len
              
              
                (
              
              value
              
                )
              
              
                ==
              
              
                0
              
              
                :
              
              
                raise
              
               ValueError
              
                (
              
              
                'value cannot be empty or blank'
              
              
                )
              
              
                return
              
               value 

            
          

在上述代碼中:

  1. AutoStorage 描述符類的 set 方法不做任何驗(yàn)證。
  2. Validated 不但 繼承了 AutoStorage 描述符類 而且還是一個(gè)抽象類。
  3. 重寫了 __set__ magic 方法 ,并將 value 設(shè)為經(jīng)過 validate 方法驗(yàn)證過的值。
  4. 經(jīng)過 驗(yàn)證后的 value 可以直接委托給父類 AutoStorage 描述符類直接存儲了
  5. 設(shè)置 validate 方法為抽象方法、 其由子類來覆蓋它
  6. Quantity 描述符類重寫了 validate 方法, 驗(yàn)證值是否大于零。
  7. NonBlank 描述分類重寫了 validate 方法, 驗(yàn)證值是否為空。

如此一來 Fruits 類的方法體中,只需要增加一行代碼即可:

            
              
                class
              
              
                Fruits
              
              
                :
              
              
    description 
              
                =
              
               NonBlank
              
                (
              
              
                "description"
              
              
                )
              
              
    weight 
              
                =
              
               Quantity
              
                (
              
              
                "weight"
              
              
                )
              
              
    price 
              
                =
              
               Quantity
              
                (
              
              
                "price"
              
              
                )
              
              
                def
              
              
                __init__
              
              
                (
              
              self
              
                ,
              
               price
              
                ,
              
               weight
              
                ,
              
               description
              
                )
              
              
                :
              
              
                # 水果的描述
              
              
        self
              
                .
              
              description 
              
                =
              
               description
        
              
                # 水果的價(jià)格
              
              
        self
              
                .
              
              price 
              
                =
              
               price
        
              
                # 水果的重量
              
              
        self
              
                .
              
              weight 
              
                =
              
               weight

    
              
                def
              
              
                subtotal
              
              
                (
              
              self
              
                )
              
              
                :
              
              
                # 小記
              
              
                return
              
               self
              
                .
              
              price 
              
                *
              
               self
              
                .
              
              weight

            
          

上述的諸多描述符類通常放在單獨(dú)的 model 模塊中,以供多個(gè)模塊共同使用。

例如,產(chǎn)品經(jīng)理小姐姐有一天和你說,甲方也想賣酸奶,需要給酸奶寫一個(gè)類; 那么此時(shí) model 模塊中的 NonBlank 和 Quantity 也能夠提供給酸奶類使用了。

這就是設(shè)計(jì)模式的魔力,其能夠減少大量的代碼冗余

若我們將諸多描述符類放在單獨(dú)的 model 模塊中、那么 Fruits 代碼看起來會是這樣:

            
              
                import
              
               de_model 
              
                as
              
               model

              
                class
              
              
                Fruits
              
              
                :
              
              
    description 
              
                =
              
               model
              
                .
              
              NonBlank
              
                (
              
              
                "description"
              
              
                )
              
              
    weight 
              
                =
              
               model
              
                .
              
              Quantity
              
                (
              
              
                "weight"
              
              
                )
              
              
    price 
              
                =
              
               model
              
                .
              
              Quantity
              
                (
              
              
                "price"
              
              
                )
              
              
                .
              
              
                .
              
              
                .
              
               以下省略 
              
                .
              
              
                .
              
              
                .
              
            
          

如果你學(xué)過 Django,那么你會意識到這和 Django ORM 中的 models.TextField() 用法一致。其實(shí) Django 的 models.TextField() 就是通過屬性描述符來實(shí)現(xiàn)的。

models.TextField() 不需要傳入托管屬性名。 其原理是類裝飾器 ,我會在后幾篇博文中提到。

總結(jié)

使用 python 特性能夠很好的管理需要特殊存儲邏輯的實(shí)例屬性。

但是、當(dāng)大量的屬性都需要同樣的存儲邏輯時(shí)、單純的使用 property 依舊會引起代碼冗余。
此時(shí)、應(yīng)該考慮是使用屬性描述符還是實(shí)現(xiàn)特性工廠函數(shù) 來解決這個(gè)問題。

我給出的建議是:在這種情況下, 盡量使用屬性描述符、因?yàn)槟悴恢篮罄m(xù)會不會有類似但又不同的屬性存取邏輯。 (例如本博文中的 description 和 weight)

使用屬性描述符比之特性工廠有著很大的優(yōu)勢、因?yàn)槠涫穷悾梢詫?shí)現(xiàn)眾多的面向?qū)ο蟮脑O(shè)計(jì)模式。

你可能已經(jīng)注意到,在本博文中,對于 property 的描述,從來都是 一種 屬性描述符。那么,**除了本博文描述的屬性描述符,還有其他類型的屬性描述符嗎?它們又擁有怎樣的特性?**下一篇博文將會解答此問題。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論