讀書筆記(3): 編寫高質(zhì)量python代碼的59個有效方法

前言

《編寫高質(zhì)量python代碼的59個有效方法》這本書分類逐條地介紹了編寫python代碼的有效思路和方法,對理解python和提高編程效率有一定的幫助。本筆記簡要整理其中的重要方法。

承接上文http://www.itdecent.cn/p/15a6050220e6
http://www.itdecent.cn/p/1f6a2b3b502e

本篇介紹關(guān)于元類及屬性的編程方法

4.元類(MetaClass)及屬性

Python中的元類概念是一種比較模糊的概念,簡單來講就是可以把python中的class語句轉(zhuǎn)譯為元類,令其在每次定義具體的類時都提供獨特的行為。

Python還具有一個奇妙的特性:可以動態(tài)地定義對屬性的訪問操作,這種動態(tài)屬性也有可能帶來令人意外的副作用。

用純屬性取代get和set方法

# C++等其他語言常用寫法

# 在類中明確實現(xiàn)getter和setter
class OldResistor(object):
    def __init__(self,ohms):
        self._ohms=ohms
    def get_ohms(self):
        return self._ohms
    def set_ohms(self,ohms):
        self._ohms=ohms
r0=OldResistor(5)
print(r0.get_ohms())
r0.set_ohms(10)
print(r0.get_ohms())
r0.set_ohms(r0.get_ohms()+5)

這種自定義獲取和設(shè)置的操作,在其他語言中很常見,但在python中不需要手工實現(xiàn),直接從public屬性開始:

class Resistor(object):
    def __init__(self,ohms):
        self.ohms=ohms
        self.vol=0
        self.current=0
r1=Resistor(5)
 
print(r1.ohms) # 5
r1.ohms=10
print(r1.ohms) # 10

此外,還可以通過@property和setter來實現(xiàn)一些特殊操作,如下所示,在給成員變量_vol賦值的同時,還進行了其他變量的修改。

class VolResistance(Resistor):
    def __init__(self,ohms):
        super().__init__(ohms)
        self._vol=0
    @property
    def vol(self):
        return self._vol
    
    @vol.setter
    def vol(self,vol):
        self._vol=vol
        self.current=self._vol/self.ohms
r2=VolResistance(5)
print(r2.current) # 0
r2.vol=10
print(r2.current) #2.0

@property和setter合作實現(xiàn)對類成員屬性的修改,@property相當于getter操作,獲取成員信息,而同時會默認創(chuàng)建相應的xx.setter裝飾器,可以動態(tài)地對相應的成員屬性進行修改。

用描述符來改寫需要復用的@property方法

內(nèi)置的@property修飾器存在不便于復用的問題,受它修飾的方法無法為同一類中的其他屬性所復用。

Python提供了描述符(descriptor)來做,對訪問操作進行一定轉(zhuǎn)譯:描述符類提供getset方法

class Grade(object):
    def __get__(*args,**kwargs):
        pass
    def __set__(*args,**kwargs):
        pass

class Exam(object):
    math_grade=Grade()
    writing_grade=Grade()
    science_grade=Grade()

在具體使用中,我們可以將每個實例對應的值記錄到Grade描述符類中,使用字典保存每個實例的狀態(tài)

class Grade(object):
    def __init__(self):
        self._values={}
        
    def __get__(self,instance,instance_type):
        print(self._values)
        if instance is None:
            return self
        return self._values.get(instance,0)
    def __set__(self,instance,value):
        self._values[instance]=value

class Exam(object):
    math_grade=Grade()
    writing_grade=Grade()
    science_grade=Grade()

exam=Exam()
exam.science_grade=40
print(exam.science_grade)
#{<__main__.Exam object at 0x7f1b6281ab90>: 40}
#40
#

這種寫法存在一個嚴重的問題:泄露內(nèi)存;在程序的生命期內(nèi),對于傳給set的每個Exam實例,_values字典都會保存指向該實例的一份引用,導致該實例的引用計數(shù)無法將為0,使得內(nèi)存無法被回收。 可以使用內(nèi)置的weakref模塊,使用WeakKeyDictionary的特殊字典,替代普通的字典。

from weakref import WeakKeyDictionary
class Grade(object):
    def __init__(self):
        self._values=WeakKeyDictionary()
        
    def __get__(self,instance,instance_type):
        print(self._values)
        if instance is None:
            return self
        return self._values.get(instance,0)
    def __set__(self,instance,value):
        self._values[instance]=value

用_getattr_ /_getattribute_ /_setattr_實現(xiàn)需求

Python提供了一些掛鉤(Hook),輔助通用代碼的編寫,將多個系統(tǒng)粘合起來。

例如:把數(shù)據(jù)庫的行表示為Python對象,往往操作時優(yōu)勢我們不清楚行的結(jié)構(gòu),需要些通用的代碼結(jié)構(gòu)。

class DB(object):
    def __init__(self):
        self.exists=5
    def __getattr__(self,name):
        value='Value for %s'%name
        setattr(self,name,value)
        return value
data=DB()
print(data.__dict__)  ## {'exists': 5}
print(data.foo)
print(data.__dict__) ## {'exists': 5, 'foo': 'Value for foo'}

在訪問data缺少的foo屬性時,會調(diào)用定義的getattr方法,從而修改實例的dict字典;這種行為適合實現(xiàn)無結(jié)構(gòu)數(shù)據(jù)的按需訪問,初次執(zhí)行getattr把相關(guān)的屬性加載進來。

然而有些情況下,我們需要動態(tài)地訪問對象屬性,使用_getattr_會導致屬性加載后一直存在屬性字典中;因此可以使用另一個方法:_getattribute_,每次訪問對象屬性時都會觸發(fā)該方法,即使屬性已經(jīng)存在與屬性字典中。

此外,可以用內(nèi)置的hashattr函數(shù)判斷對象是否已經(jīng)擁有了相關(guān)的屬性,并用內(nèi)置的getattr函數(shù)來獲取屬性值。

在利用獲取方法得到具體屬性,可以通過_setattr_進行屬性的賦值操作。

此外值得注意的一點是,要避免通過_getattribute_和_setattr_方法中訪問實例屬性,容易造成無限遞歸。

這個時候要采用super()._get__attribute_方法,從實例的屬性字典里面直接獲取_data屬性值,以避免無限遞歸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容