读书笔记:《流畅的Python》第21章 类元编程
生活随笔
收集整理的這篇文章主要介紹了
读书笔记:《流畅的Python》第21章 类元编程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
# 第21章 類元編程"""
類元編程指的是運行時創建或定制類的技藝1.類是一等對象,任何時候都可以使用函數新建類,而無需使用class關鍵字2.類裝飾器也是函數,不過能夠審查/修改/甚至把被裝飾的類替換成其他類3.元類功能強大,但難以掌握,類裝飾器能用更簡單的方式解決很多問題
"""# 21.1 類工廠函數
# collections.namedtuple:
# 把一個類名和幾個屬性名傳給這個函數,它會創建一個tuple的子類,
# 其中的元素通過名稱獲取,還為調式提供了友好的字符串表示形式(__repr__)
# 使用類似于collections.namedtuple工廠函數的方式,建立一個類工廠函數
# 21-2 record_factory.py:一個簡單的類工廠函數# type:
"""
通常,我們把type()視為函數,調用type(my_object)獲取對象所屬的類
效果和my_object.__class__一樣,
然而type是一個類,當作類使用時傳入三個參數可以新建一個類:MyClass = type('MyClass',(MySuperClass,MyMixin),{'x':42,'x2':lambda self :self.x*2})
三個參數分別是 name/base/dictdict是映射,指定新類的屬性和值上述代碼等價于class MyClass(MySuperClass,MyMixin):x = 42def x2(slef):return self.x *2
type的實例是類,type是自身的實例
把三個參數傳給type是動態創建類的常用方式
collctions.namedtuple使用的是另一種方式:先申明一個_class_template變量,其值是字符串形式的源碼模版,然后namedtuple函數調用_class_template的format方法,填充模版里的空白最后使用內置的exec函數計算得到的源碼字符串
"""# 21.2 定制描述符的類裝飾器
"""
類裝飾器和函數裝飾非常類似,是參數為類對象的函數,返回原來的類或修改后的類"""# 示例 21-3 bulkfood_v6.py:使用Quantity和NonBlank描述符的LineItem類
# 示例 21-4 model_v6.py :一個簡單的類裝飾器"""
類裝飾器有個重大缺陷:只對直接依附的類有效,被裝飾的類的子類可能繼承也可能不會繼承裝飾器所做的改動具體情況視改動的方式而定
"""# 21.3導入時和運行時的比較
"""
導入時和運行時:python解釋器會從上到下一次性解析完.py的源碼,然后生成用于執行的字節碼如果句法有錯誤,就在此時報告如果本地的__pycache__文件夾中有最新的.pyc文件,則跳過上述步驟,因為已經有運行所需的字節碼了編譯肯定是導入時的活動,不過那個時期還會做其他事情,因為python語句幾乎都是可執行的,也就是說語句可能會運行用戶代碼,修改用戶程序狀態,尤其是import語句,它不只是申明在用戶首次導入模塊時,還會運行模塊中的全部頂層代碼--以后帶入相同的模塊則使用緩存,只做名稱綁定.哪些頂層代碼可以做任何事情,包括通常在運行時做的事情,比如連接數據庫對于模塊中的函數.運行頂層代碼時,會編譯函數的定義體,但并不會執行,僅當在運行時調用函數才執行對于類來說,情況就不同了在導入時,解釋器會執行每個類的定義體,甚至會執行嵌套類的定義體結果是定義了類的屬性和方法,并構建了類對象
"""# 理解計算時間的練習
# 示例21-6 evaltime.py 按順序寫出輸出的序號標記<[N]># 21.4 元類的基礎知識
"""
元類是制造類的工廠,不過不是函數而是類
根據Python對象模型,類是對象,因此類肯定是另外某個類的實例
默認情況下,python中的類是type類的實例,即
type類是大多數內置的類和用戶自定義的類的元類object和type的關系:object是type的實例,type是object的子類所有的類都直接或者間接地是type的實例,不過只有元類同時也是type的子類
"""
print('spam'.__class__) # <class 'str'>
print(str.__class__) # <class 'type'>
print(type.__class__) # <class 'type'> type是其自身的實例# 其他元類 abc.ABCMeta
import collectionsprint(collections.Iterable.__class__) # <class 'abc.ABCMeta'>
import abcprint(abc.ABCMeta.__class__) # <class 'type'>
print(abc.ABCMeta.__mro__)"""
元類可以通過實現__init__方法定制實例,元類的__init__方法可以做到
類裝飾器能做的任何事情,但是作用更大"""# 理解元類計算時間的練習
# 示例21-10 evaltime_meta.py :ClassFive是MetaAleph元類的實例# 21.5 定制描述符的元類
# 示例 21-14 bulkfood_v7.py : 有元類支持,繼承model.Entity類即可
# 示例 21-15 model_v7.py:EntityMeta元類以及它的一個實例# 21.6元類的特殊方法__prepare__
# 示例21-16 model_v8.py:這一版的EntityMeta用到了__prepare__方法,而且為Entity定義了field_names類方法"""
元類的作用:1.驗證屬性2.一次把裝飾器依附到多個方法上3.序列化對象或者轉換數據4.對象關系映射5.基于對象的持久存儲6.動態轉換使用其他語言編寫的類結構
"""#21.7 類作為對象
"""
python數據模型為每個類定義了很多屬性__mro____class____name__cls.__bases:由類的基類組成的元組cls.__qualname__:其值是類或者函數的限定名稱,即從模塊的全局作用域到類的點分路徑cls.__subclasses__():這個方法返回一個列表,包含類的直接子類,這個方法的實現使用弱引用cls.mro():構建類時,如果需要獲取存儲在類屬性__mro__中的超類元組,解釋器會調用這個方法,定制要構建類的解析方法順序"""
# 21-2 record_factory.py:一個簡單的類工廠函數
# 21-2 record_factory.py:一個簡單的類工廠函數
def record_factory(cls_name,field_names):try:field_names = field_names.replace(',',' ').split() # 1except AttributeError: # 不能調用replace或者split方法pass # 假定field_names本就是標識符組成的序列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"""
說明:1.這里體現了鴨子類型:嘗試在逗號或者空格拆分field_names,如果失敗,那么假定field_names本就是可迭代的對象,一個元素對應一個屬性名2.使用屬性名構建元組,浙江成為新建類的__slots__屬性;此外這個么做,還設定了拆包的順序和字符串表示形式中各字段的順序.3.這個函數將成為新建類的__init__方法,參數有位置參數或關鍵字參數4.實現了__iter__方法,把類的實例變成可迭代的對象;按照__slots__的順序產出字段值5.迭代__slots__和self,生成友好的字符串表示形式6.組建類屬性字典7.調用type構造方法,構建新類,返回將其返回"""
if __name__ == '__main__':Dog = record_factory('Dog','name weight owner')rex = Dog('REX',30,'Bob','color'=='white') # 可以這樣寫,但是不能把屬性加入實例的屬性# print(rex.color) # 報錯print(repr(rex))# print(rex.__slots__)# print(Dog.__dict__)name,weight,_ = rex # 實例是可迭代的對象print(name,weight)print("{2}'s dog weight {1}kg".format(*rex)) # 也可以拆包rex.weight = 32 # 實例是可變的對象print(repr(rex))print(Dog.__mro__) # 新類繼承自object和工廠函數沒有關系
# 示例 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 :一個簡單的類裝飾器
# 示例 21-4 model_v6.py :一個簡單的類裝飾器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 # 如果不是通過實例調用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗證操作委托給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 要求具體的方法返回驗證后的值,借機可以清理\轉換和規范化接收的數據
"""===============以上為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"""
說明:1.裝飾器的參數是一個類2.迭代存儲類屬性的字典3.如果屬性是Validated描述符實例4.使用買描述符類的托管屬性的名稱命名storage_name(例如_NonBlank#description)5.返回修改后的類"""
# 示例21-6 evaltime.py 按順序寫出輸出的序號標記<[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元類的實例
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元類以及它的一個實例
# 示例 21-15 model_v7.py:EntityMeta元類以及它的一個實例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 # 如果不是通過實例調用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗證操作委托給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 要求具體的方法返回驗證后的值,借機可以清理\轉換和規范化接收的數據
"""===============以上為modle_v5.py的代碼=================="""class EntityMeta(type):'''元類,用于創建帶驗證字段的業務實體'''def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict) # 1 在超類type上調用__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): # 這個類的存在只是為了用起來便利,這個模塊的用戶直接繼承Entity即可'''帶有驗證字段的實體業務'''
# 示例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元類以及它的一個實例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 # 如果不是通過實例調用,返回描述符自身else: # 否則像之前一樣返回托管屬性的值return getattr(instance,self.storage_name)def __set__(self, instance, value):setattr(instance,self.storage_name,value) #2 ..驗證部分除外class Validated(abc.ABC,AutoStorage): # 3 抽象類也繼承AutoStoragedef __set__(self, instance, value):value = self.validate(instance,value) # 4 把驗證操作委托給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 要求具體的方法返回驗證后的值,借機可以清理\轉換和規范化接收的數據
"""===============以上為modle_v5.py的代碼=================="""class EntityMeta(type):'''元類,用于創建帶驗證字段的業務實體'''@classmethoddef __prepare__(cls, name, bases):return collections.OrderedDict() # 返回一個空的OrderedDict實例,類屬性將存儲在里面def __init__(cls,name,bases,attr_dict):super().__init__(name,bases,attr_dict)cls._field_names = [] # 在要構建的類中創建一個_field_names屬性for key,attr in attr_dict.items(): # 這里的 attr_dict是一個OrderedDict對象,會按順序迭代if isinstance(attr,Validated):type_name = type(attr).__name__attr.storage_name = '_{}#{}'.format(type_name,key)cls._field_names.append(key) # 把找到的各個Validated字段添加到_field_names中
class Entity(metaclass=EntityMeta): # 這個類的存在只是為了用起來便利,這個模塊的用戶直接繼承Entity即可'''帶有驗證字段的實體業務'''@classmethoddef field_names(cls):for name in cls._field_names:yield name # 按照添加字段的順序產出字段的名稱
35歲學python,也不知道為了啥?
總結
以上是生活随笔為你收集整理的读书笔记:《流畅的Python》第21章 类元编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS中的5款小众黑科技APP!用了都舍
- 下一篇: 如何创建 Python 库