用Descriptor來實現類級屬性(Property)

發表於2016-01-26

上篇文章簡單介紹了python中描述器(Descriptor)的概念和使用,有心的同學估計已經Get√了該技能。本篇文章通過一個Descriptor的使用場景再次給出一個案例,讓不瞭解情況的同學可以更容易理解。

先說說decorator

這兩個單詞確實是有些相似,同時在使用中也是形影不離。這也給人造成了理解上的困難,說裝飾器和描述器到底是怎麼回事,為什麼非得用一個@符號再加上描述器才行。

很多文章也都把這倆結合著講,我自己看完之後都會覺得很繞。其實學習一個知識點,和做專案開發一個功能是一樣的。在功能拆分的時候我們都會盡量的把任務拆分的足夠小,然後才分配到開發者頭上。這樣保證各個任務的獨立性,完整性,並且易於做進度管理。在任務開發的時候也不能把你的任務都放到一個函式/介面中去做,以避免各功能間產生高耦合的狀況,導致後期難以維護。

再說回到學習一個技術點,如果你總是嘗試一下子就要掌握兩個或多個技術點,結果可能是忙活了半天,發現還是暈頭轉向。

擦,好像扯遠了。

說Descriptor是Descriptor, Decorator是Decorator,遇到不懂的地方,各個擊破,哪裡不懂點哪裡。所以先說Decorator, 關鍵點是你要意識到這就是一個語法糖 。所謂語法糖就是讓你可以用簡單的方式寫程式碼。本質上裝飾器(Decorator)就是這樣:

正題:通過Descriptor來做一個類級的Property

常見的Property是這樣的:

這中property的使用,是例項級的應用。因為只有在 foo = Foo() 之後,才可以 foo.name

但是如果我需要一個類級的屬性應該怎麼做呢,就像是 classmethod一樣,不需要例項化類我就可以呼叫。對應的需求是這樣的,定義了一個基類DBManage:

這其實一個對應著資料庫中某張表的基礎模型,我希望其他的Model都來繼承它,然後可以重用這個table_name的方法(目前還是方法)。

我只需要這麼定義User模型即可:

然後這麼定義Post模型:

這樣我如果需要查所有的User資料,只需要 User.select_all() 即可,同理Post也是如此 Post.select_all() 。但此時發現一個有點不爽的事情。那就是基類中的 cls.table_name() 這個程式碼,table_name看起來就是屬性,卻需要用呼叫方法的方式獲取。不妥。

於是自定義了一個classproperty:

這需要這樣,我在DBManage中的程式碼就可以改為:

這就是Descriptor另外的一個使用案例了。

可能有人或有一個小疑問:為毛你不是在sql賦值時直接 sql = "SELECT * FROM %s" % cls.__name__.lower() 。這個問題,問的非常好,原因就一個字:懶。懶得以後每次都得敲那麼多程式碼。

相關文章