日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

读书笔记:《流畅的Python》第21章 类元编程

發(fā)布時(shí)間:2023/12/8 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读书笔记:《流畅的Python》第21章 类元编程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
# 第21章 類元編程""" 類元編程指的是運(yùn)行時(shí)創(chuàng)建或定制類的技藝1.類是一等對(duì)象,任何時(shí)候都可以使用函數(shù)新建類,而無(wú)需使用class關(guān)鍵字2.類裝飾器也是函數(shù),不過(guò)能夠?qū)彶?修改/甚至把被裝飾的類替換成其他類3.元類功能強(qiáng)大,但難以掌握,類裝飾器能用更簡(jiǎn)單的方式解決很多問(wèn)題 """# 21.1 類工廠函數(shù) # collections.namedtuple: # 把一個(gè)類名和幾個(gè)屬性名傳給這個(gè)函數(shù),它會(huì)創(chuàng)建一個(gè)tuple的子類, # 其中的元素通過(guò)名稱獲取,還為調(diào)式提供了友好的字符串表示形式(__repr__) # 使用類似于collections.namedtuple工廠函數(shù)的方式,建立一個(gè)類工廠函數(shù) # 21-2 record_factory.py:一個(gè)簡(jiǎn)單的類工廠函數(shù)# type: """ 通常,我們把type()視為函數(shù),調(diào)用type(my_object)獲取對(duì)象所屬的類 效果和my_object.__class__一樣, 然而type是一個(gè)類,當(dāng)作類使用時(shí)傳入三個(gè)參數(shù)可以新建一個(gè)類:MyClass = type('MyClass',(MySuperClass,MyMixin),{'x':42,'x2':lambda self :self.x*2}) 三個(gè)參數(shù)分別是 name/base/dictdict是映射,指定新類的屬性和值上述代碼等價(jià)于class MyClass(MySuperClass,MyMixin):x = 42def x2(slef):return self.x *2 type的實(shí)例是類,type是自身的實(shí)例 把三個(gè)參數(shù)傳給type是動(dòng)態(tài)創(chuàng)建類的常用方式 collctions.namedtuple使用的是另一種方式:先申明一個(gè)_class_template變量,其值是字符串形式的源碼模版,然后namedtuple函數(shù)調(diào)用_class_template的format方法,填充模版里的空白最后使用內(nèi)置的exec函數(shù)計(jì)算得到的源碼字符串 """# 21.2 定制描述符的類裝飾器 """ 類裝飾器和函數(shù)裝飾非常類似,是參數(shù)為類對(duì)象的函數(shù),返回原來(lái)的類或修改后的類"""# 示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem類 # 示例 21-4 model_v6.py :一個(gè)簡(jiǎn)單的類裝飾器""" 類裝飾器有個(gè)重大缺陷:只對(duì)直接依附的類有效,被裝飾的類的子類可能繼承也可能不會(huì)繼承裝飾器所做的改動(dòng)具體情況視改動(dòng)的方式而定 """# 21.3導(dǎo)入時(shí)和運(yùn)行時(shí)的比較 """ 導(dǎo)入時(shí)和運(yùn)行時(shí):python解釋器會(huì)從上到下一次性解析完.py的源碼,然后生成用于執(zhí)行的字節(jié)碼如果句法有錯(cuò)誤,就在此時(shí)報(bào)告如果本地的__pycache__文件夾中有最新的.pyc文件,則跳過(guò)上述步驟,因?yàn)橐呀?jīng)有運(yùn)行所需的字節(jié)碼了編譯肯定是導(dǎo)入時(shí)的活動(dòng),不過(guò)那個(gè)時(shí)期還會(huì)做其他事情,因?yàn)閜ython語(yǔ)句幾乎都是可執(zhí)行的,也就是說(shuō)語(yǔ)句可能會(huì)運(yùn)行用戶代碼,修改用戶程序狀態(tài),尤其是import語(yǔ)句,它不只是申明在用戶首次導(dǎo)入模塊時(shí),還會(huì)運(yùn)行模塊中的全部頂層代碼--以后帶入相同的模塊則使用緩存,只做名稱綁定.哪些頂層代碼可以做任何事情,包括通常在運(yùn)行時(shí)做的事情,比如連接數(shù)據(jù)庫(kù)對(duì)于模塊中的函數(shù).運(yùn)行頂層代碼時(shí),會(huì)編譯函數(shù)的定義體,但并不會(huì)執(zhí)行,僅當(dāng)在運(yùn)行時(shí)調(diào)用函數(shù)才執(zhí)行對(duì)于類來(lái)說(shuō),情況就不同了在導(dǎo)入時(shí),解釋器會(huì)執(zhí)行每個(gè)類的定義體,甚至?xí)?zhí)行嵌套類的定義體結(jié)果是定義了類的屬性和方法,并構(gòu)建了類對(duì)象 """# 理解計(jì)算時(shí)間的練習(xí) # 示例21-6 evaltime.py 按順序?qū)懗鲚敵龅男蛱?hào)標(biāo)記<[N]># 21.4 元類的基礎(chǔ)知識(shí) """ 元類是制造類的工廠,不過(guò)不是函數(shù)而是類 根據(jù)Python對(duì)象模型,類是對(duì)象,因此類肯定是另外某個(gè)類的實(shí)例 默認(rèn)情況下,python中的類是type類的實(shí)例,即 type類是大多數(shù)內(nèi)置的類和用戶自定義的類的元類object和type的關(guān)系:object是type的實(shí)例,type是object的子類所有的類都直接或者間接地是type的實(shí)例,不過(guò)只有元類同時(shí)也是type的子類 """ print('spam'.__class__) # <class 'str'> print(str.__class__) # <class 'type'> print(type.__class__) # <class 'type'> type是其自身的實(shí)例# 其他元類 abc.ABCMeta import collectionsprint(collections.Iterable.__class__) # <class 'abc.ABCMeta'> import abcprint(abc.ABCMeta.__class__) # <class 'type'> print(abc.ABCMeta.__mro__)""" 元類可以通過(guò)實(shí)現(xiàn)__init__方法定制實(shí)例,元類的__init__方法可以做到 類裝飾器能做的任何事情,但是作用更大"""# 理解元類計(jì)算時(shí)間的練習(xí) # 示例21-10 evaltime_meta.py :ClassFive是MetaAleph元類的實(shí)例# 21.5 定制描述符的元類 # 示例 21-14 bulkfood_v7.py : 有元類支持,繼承model.Entity類即可 # 示例 21-15 model_v7.py:EntityMeta元類以及它的一個(gè)實(shí)例# 21.6元類的特殊方法__prepare__ # 示例21-16 model_v8.py:這一版的EntityMeta用到了__prepare__方法,而且為Entity定義了field_names類方法""" 元類的作用:1.驗(yàn)證屬性2.一次把裝飾器依附到多個(gè)方法上3.序列化對(duì)象或者轉(zhuǎn)換數(shù)據(jù)4.對(duì)象關(guān)系映射5.基于對(duì)象的持久存儲(chǔ)6.動(dòng)態(tài)轉(zhuǎn)換使用其他語(yǔ)言編寫的類結(jié)構(gòu) """#21.7 類作為對(duì)象 """ python數(shù)據(jù)模型為每個(gè)類定義了很多屬性__mro____class____name__cls.__bases:由類的基類組成的元組cls.__qualname__:其值是類或者函數(shù)的限定名稱,即從模塊的全局作用域到類的點(diǎn)分路徑cls.__subclasses__():這個(gè)方法返回一個(gè)列表,包含類的直接子類,這個(gè)方法的實(shí)現(xiàn)使用弱引用cls.mro():構(gòu)建類時(shí),如果需要獲取存儲(chǔ)在類屬性__mro__中的超類元組,解釋器會(huì)調(diào)用這個(gè)方法,定制要構(gòu)建類的解析方法順序""" # 21-2 record_factory.py:一個(gè)簡(jiǎn)單的類工廠函數(shù) # 21-2 record_factory.py:一個(gè)簡(jiǎn)單的類工廠函數(shù) def record_factory(cls_name,field_names):try:field_names = field_names.replace(',',' ').split() # 1except AttributeError: # 不能調(diào)用replace或者split方法pass # 假定field_names本就是標(biāo)識(shí)符組成的序列field_names = tuple(field_names) # 2def __init__(self,*args,**kwargs): # 3attrs = dict(zip(self.__slots__,args))attrs.update(kwargs)for name,value in attrs.items():setattr(self,name,value)def __iter__(self): # 4for name in self.__slots__:yield getattr(self,name)def __repr__(self): # 5values = ', '.join('{}={!r}'.format(*i)for i in zip(self.__slots__,self))return '{}({})'.format(self.__class__.__name__,values)cls_attrs = dict(__slots__ = field_names, # 6__init__ = __init__,__iter__ = __iter__,__repr__ = __repr__)return type(cls_name,(object,),cls_attrs) # 7""" 說(shuō)明:1.這里體現(xiàn)了鴨子類型:嘗試在逗號(hào)或者空格拆分field_names,如果失敗,那么假定field_names本就是可迭代的對(duì)象,一個(gè)元素對(duì)應(yīng)一個(gè)屬性名2.使用屬性名構(gòu)建元組,浙江成為新建類的__slots__屬性;此外這個(gè)么做,還設(shè)定了拆包的順序和字符串表示形式中各字段的順序.3.這個(gè)函數(shù)將成為新建類的__init__方法,參數(shù)有位置參數(shù)或關(guān)鍵字參數(shù)4.實(shí)現(xiàn)了__iter__方法,把類的實(shí)例變成可迭代的對(duì)象;按照__slots__的順序產(chǎn)出字段值5.迭代__slots__和self,生成友好的字符串表示形式6.組建類屬性字典7.調(diào)用type構(gòu)造方法,構(gòu)建新類,返回將其返回""" if __name__ == '__main__':Dog = record_factory('Dog','name weight owner')rex = Dog('REX',30,'Bob','color'=='white') # 可以這樣寫,但是不能把屬性加入實(shí)例的屬性# print(rex.color) # 報(bào)錯(cuò)print(repr(rex))# print(rex.__slots__)# print(Dog.__dict__)name,weight,_ = rex # 實(shí)例是可迭代的對(duì)象print(name,weight)print("{2}'s dog weight {1}kg".format(*rex)) # 也可以拆包rex.weight = 32 # 實(shí)例是可變的對(duì)象print(repr(rex))print(Dog.__mro__) # 新類繼承自object和工廠函數(shù)沒有關(guān)系 # 示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem類 # 示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem類import model_v6 as model @model.entity #添加了類裝飾器 class LineItem:weight = model.Quantity()price = model.Quantity()description = model.NonBlank()def __init__(self,description,weight,price):self.description = descriptionself.weight = weightself.price = pricedef subtotal(self):return self.weight * self.priceif __name__ == '__main__':raisins = LineItem('Golden raisins',10,6.95)print(dir(raisins)) # '_NonBlank#description', '_Quantity#price', '_Quantity#weight'print(LineItem.description.storage_name) #_NonBlank#descriptionprint(raisins.description)print(getattr(raisins, '_NonBlank#description')) # 示例 21-4 model_v6.py :一個(gè)簡(jiǎn)單的類裝飾器 # 示例 21-4 model_v6.py :一個(gè)簡(jiǎn)單的類裝飾器import abc class AutoStorage: #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管類的引用if instance is None:return self # 如果不是通過(guò)實(shí)例調(diào)用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗(yàn)證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗(yàn)證操作委托給validate方法super().__set__(instance,value) # 5 然后把返回的值傳給超類的__set__方法@abc.abstractmethoddef validate(selfs,instance,value): # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated): # 7 Quantity和NonBlank都繼承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value # 8 要求具體的方法返回驗(yàn)證后的值,借機(jī)可以清理\轉(zhuǎn)換和規(guī)范化接收的數(shù)據(jù) """===============以上為modle_v5.py的代碼=================="""def entity(cls): # 1for key,attr in cls.__dict__.items(): # 2if isinstance(attr,Validated): # 3type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key) # 4return cls # 5""" 說(shuō)明:1.裝飾器的參數(shù)是一個(gè)類2.迭代存儲(chǔ)類屬性的字典3.如果屬性是Validated描述符實(shí)例4.使用買描述符類的托管屬性的名稱命名storage_name(例如_NonBlank#description)5.返回修改后的類""" # 示例21-6 evaltime.py 按順序?qū)懗鲚敵龅男蛱?hào)標(biāo)記<[N]> from evalsupport import deco_alpha print('<[1]> evaltime module start ') class ClassOne:print('<[2]> CalssOne body ')def __init__(self):print('<[3]> CalssOne.__init__')def __del__(self):print('<[4]> CalssOne.__del__')def method_x(self):print('<[5]> CalssOne.method_x')class ClassTwo(object):print('<[6]> CalssTwo body ')@deco_alpha class ClassThree():print('<[7]> ClassThree body ')def method_y(self):print('<[8]> CalssThree.method_y')class ClassFour(ClassThree):print('<[9]> ClassFour body ')def method_y(self):print('<[10]> CalssFour.method_y')if __name__ == '__main__':print('<[11]> ClassOne tests',30*".")one = ClassOne()one.method_x()print('<[12]> ClassThree tests', 30 * ".")three = ClassThree()three.method_y()print('<[13]> ClassFour tests', 30 * ".")four = ClassFour()four.method_y()print('<[14]> evaltime module end') evalsupport.py print('<[100]> evalsuport module start') def deco_alpha(cls):print('<[200]> deco_alpha')def inner_1(self):print('<[300]> deco_alpha:inner_1')cls.method_y = inner_1return clsclass MetaAleph(type):print('<[400]> MetaAleph body')def __init__(cls,name,bases,dic):print('<[500]> MetaAleph.__init__')def inner_2(self):print('<[600]> MetaAleph.__init__:inner_2')cls.method_z = inner_2 print('<[700]> evalsuport module end') # 示例21-10 evaltime_meta.py :ClassFive是MetaAleph元類的實(shí)例 from evalsupport import deco_alpha from evalsupport import MetaAlephprint('<[1]> evaltime_meta module start ') @deco_alpha class ClassThree():print('<[2]> ClassThree body ')def method_y(self):print('<[3]> CalssThree.method_y')class ClassFour(ClassThree):print('<[4]> ClassFour body ')def method_y(self):print('<[5]> CalssFour.method_y')class ClassFive(metaclass=MetaAleph):print('<[6]> ClassFive body ')def method_y(self):print('<[7]> CalssFive.method_y')def method_z(self):print('<[8]> CalssFive.method_z')class ClassSix(ClassFive):print('<[9]> ClassSix body ')def method_z(self):print('<[10]> CalssSix.method_z')if __name__ == '__main__':print('<[11]> ClassThree tests',30*".")three = ClassThree()three.method_y()print('<[12]> ClassFour tests', 30 * ".")four = ClassFour()four.method_y()print('<[13]> ClassFive tests', 30 * ".")five = ClassFive()five.method_z()print('<[14]> ClassSix tests', 30 * ".")six = ClassSix()six.method_z()print('<[15]> evaltime_meta module end') # 示例 21-14 bulkfood_v7.py : 有元類支持,繼承model.Entity類即可 # 示例 21-14 bulkfood_v7.py : 有元類支持,繼承model.Entity類即可# import model_v7 as model import model_v8 as model class LineItem(model.Entity): # LineItem是model.Entity的子類weight = model.Quantity()price = model.Quantity()description = model.NonBlank()def __init__(self,description,weight,price):self.description = descriptionself.weight = weightself.price = pricedef subtotal(self):return self.weight * self.priceif __name__ == '__main__':raisins = LineItem('Golden raisins',10,6.95)print(dir(raisins)) # '_NonBlank#description', '_Quantity#price', '_Quantity#weight'print(LineItem.description.storage_name) #_NonBlank#descriptionprint(raisins.description)print(getattr(raisins, '_NonBlank#description'))# model_v8的新功能for name in LineItem.field_names():print(name) # 示例 21-15 model_v7.py:EntityMeta元類以及它的一個(gè)實(shí)例 # 示例 21-15 model_v7.py:EntityMeta元類以及它的一個(gè)實(shí)例import abc class AutoStorage: #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管類的引用if instance is None:return self # 如果不是通過(guò)實(shí)例調(diào)用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗(yàn)證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗(yàn)證操作委托給validate方法super().__set__(instance,value) # 5 然后把返回的值傳給超類的__set__方法@abc.abstractmethoddef validate(selfs,instance,value): # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated): # 7 Quantity和NonBlank都繼承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value # 8 要求具體的方法返回驗(yàn)證后的值,借機(jī)可以清理\轉(zhuǎn)換和規(guī)范化接收的數(shù)據(jù) """===============以上為modle_v5.py的代碼=================="""class EntityMeta(type):'''元類,用于創(chuàng)建帶驗(yàn)證字段的業(yè)務(wù)實(shí)體'''def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict) # 1 在超類type上調(diào)用__init__方法for key,attr in attr_dict.items(): # 2 與類裝飾器的邏輯一樣if isinstance(attr,Validated):type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key) class Entity(metaclass=EntityMeta): # 這個(gè)類的存在只是為了用起來(lái)便利,這個(gè)模塊的用戶直接繼承Entity即可'''帶有驗(yàn)證字段的實(shí)體業(yè)務(wù)''' # 示例21-16 model_v8.py:這一版的EntityMeta用到了__prepare__方法,而且為Entity定義了field_names類方法 # 示例21-16 model_v8.py:這一版的EntityMeta用到了__prepare__方法,而且為Entity定義了field_names類方法 # 示例 21-15 model_v7.py:EntityMeta元類以及它的一個(gè)實(shí)例import abc import collections class AutoStorage: #1 AutoStorage提供了之前Quantity的大部分功能__counter = 0def __init__(self):cls = self.__class__prefix = cls.__name__index = cls.__counterself.storage_name = '_{}#{}'.format(prefix,index)cls.__counter += 1def __get__(self,instance,ower):# ower是托管類的引用if instance is None:return self # 如果不是通過(guò)實(shí)例調(diào)用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗(yàn)證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗(yàn)證操作委托給validate方法super().__set__(instance,value) # 5 然后把返回的值傳給超類的__set__方法@abc.abstractmethoddef validate(selfs,instance,value): # 6 validate是抽象方法'''return validated value or raise ValueError'''class Quantity(Validated): # 7 Quantity和NonBlank都繼承自Validated'''a number greater than zero'''def validate(selfs,instance,value):if value <= 0:raise ValueError('value must be > 0 ')return valueclass NonBlank(Validated):'''a string with at least one non-space character'''def validate(selfs,instance,value):value = value.strip()if len(value) == 0:raise ValueError('value cannot be empty or blank')return value # 8 要求具體的方法返回驗(yàn)證后的值,借機(jī)可以清理\轉(zhuǎn)換和規(guī)范化接收的數(shù)據(jù) """===============以上為modle_v5.py的代碼=================="""class EntityMeta(type):'''元類,用于創(chuàng)建帶驗(yàn)證字段的業(yè)務(wù)實(shí)體'''@classmethoddef __prepare__(cls, name, bases):return collections.OrderedDict() # 返回一個(gè)空的OrderedDict實(shí)例,類屬性將存儲(chǔ)在里面def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict)cls._field_names = [] # 在要構(gòu)建的類中創(chuàng)建一個(gè)_field_names屬性for key,attr in attr_dict.items(): # 這里的 attr_dict是一個(gè)OrderedDict對(duì)象,會(huì)按順序迭代if isinstance(attr,Validated):type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key)cls._field_names.append(key) # 把找到的各個(gè)Validated字段添加到_field_names中 class Entity(metaclass=EntityMeta): # 這個(gè)類的存在只是為了用起來(lái)便利,這個(gè)模塊的用戶直接繼承Entity即可'''帶有驗(yàn)證字段的實(shí)體業(yè)務(wù)'''@classmethoddef field_names(cls):for name in cls._field_names:yield name # 按照添加字段的順序產(chǎn)出字段的名稱

35歲學(xué)python,也不知道為了啥?

總結(jié)

以上是生活随笔為你收集整理的读书笔记:《流畅的Python》第21章 类元编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。