導語:本文章記錄了本人在學習Python基礎之元編程篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。
一、瞭解運行時建立類的方法——類工廠函數;
二、熟悉元類的基礎知識和使用場景;
三、瞭解元類的__prepare__的意義;
四、瞭解class的屬性以及Python解釋器如何處理導入的模塊。
類元編程是指在運行時建立或定製類的技藝。
python
類是一等對象,所以任什麼時候候均可以使用函數新建類,而無需使用class關鍵字。
編程
實例: 下面咱們寫一個簡單的類工廠函數:緩存
def record_factory(cls_name,field_names): try: field_names=field_names.replace(',',' ').split() except AttributeError: pass field_names=tuple(field_names)#使用屬性名構建元組,這將成爲新的__slots__。 def __init__(self,*args,**kwards):#經過字典解析輸入數據,而後將值賦給新建的類的屬性。 attrs=dict(zip(self.__slots__,args)) attrs.update(kwards) for key,value in attrs.items(): setattr(self,key,value) def __iter__(self):#迭代特殊方法的思想在於讓解釋器查詢到屬性對應的值。 for name in self.__slots__: yield getattr(self,name)#實現迭代的方法在於:經過內置getattr方法獲取已經初始化的類屬性對應的值。 def __repr__(self):#返回一個合適的字符串。 msg=', '.join('{}={!r}'.format(*i) for i in zip(self.__slots__,self)) return '{}({})'.format(self.__class__.__name__,msg) cls_attrs=dict(__slots__=field_names, __init__=__init__, __iter__=__iter__, __repr__=__repr__) return type(cls_name,(object,),cls_attrs)#使用type構造方法構建新類並返回,注意object後的逗號很重要。 animal=record_factory('animal','name,age,price') dog=animal('dog','Bob','1000') print(dog)#構造animal實例對象 a,b,_=dog#能夠實現預期的拆包賦值 print(a,b)
輸出:安全
animal(name='dog', age='Bob', price='1000') dog Bob
type的三個參數:class type(name, bases, dict)
type最後一個參數是一個映射,指定新類的屬性名和值。app
注意:在Python中作元編程時, 最好不要用 exec 和 eval 函數.。若是接接收的字符串來自不可信的源,這兩個函數會帶來嚴重的安全風險.框架
類裝飾器:本質上是高階函數,其參數是被裝飾的類,用於審查審查、修改、甚至把被裝飾的類替換成其餘類。函數
類裝飾器的重大缺點:只對直接依附的類有效。即被裝飾的類的子類可能繼承也可能不繼承裝飾器所作的改動,具體狀況視改動的方式而定。工具
元類:是類元編程最高級的工具:使用元類能夠建立具備某種特質的全新變種,例如抽象基類。
功能:元類是建立類的類。
特色:
學習
全部類都直接或間接地是type的實例,不過只有元類同時也是type的子類。
具體而言,元類能夠經過實現__init__方法定製類。
爲了不無限回溯,type.__class是type,即type是自身的實例。
建議:除非開發框架,不然不要在生產代碼中定義元類或抽象基類。
code
實例:使用元類定製描述符
class EntityMeta(type): """Metaclass for business entities with validated fields""" def __init__(cls, name, bases, attr_dict): super().__init__(name, bases, attr_dict) # 建立類對象。 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): # <3> """Business entity with validated fields"""
__prepare__的功能:知道類的屬性定義的順序。
使用場景
:__prepare__只在元類中有用,並且必須聲明爲類方法(即要使用@classmethod 裝飾器定義)。參數要求
:__prepare__方法的第一個參數是元類,隨後兩個參數分別是要構建的類的名稱和基類組成的元組, 返回值必須是映射。工做原理
:元類構建新類時,解釋器會先調用__prepare__ 方法,使用類定義體中的屬性建立映射。接着把__prepare__方法返回的映射會傳給__new__ 方法的最後一個參數,而後再傳給__init__ 方法。
實例:使用__prepare__
class EntityMeta(type): """Metaclass for business entities with validated fields""" @classmethod def __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(): if isinstance(attr, Validated): type_name = type(attr).__name__ attr.storage_name = '_{}#{}'.format(type_name, key) cls._field_names.append(key) class Entity(metaclass=EntityMeta): """Business entity with validated fields""" @classmethod def field_names(cls): # field_names 類方法的做用簡單:按照添加字段的順序產出字段的名稱 for name in cls._field_names: yield name
在進程中首次導入模塊時,Python解釋器還會運行所導入模塊中的所有頂層代碼。之後導入相同的模塊則使用緩存,只作名稱綁定。
對函數而言,首次導入模塊時:解釋器會執行頂層的 def 語句,編譯函數的定義體,把函數對象綁定到對應的全局名稱上,可是顯然解釋器不會執行函數的定義體。
一般這意味着解釋器在導入時定義頂層函數,可是僅當在運行時調用函數時纔會執行函數的定義體。
對類而言,首次導入模塊時:解釋器會執行每一個類的定義體,甚至會執行嵌套類的定義體。執行類定義體的結果是,定義了類的屬性和方法,並構建了類對象。
從這個意義上理解,類的定義體屬於「頂層代碼」,由於它在導入時運行。
class除了除__mro__、__class__、和__name__以外還有如下屬性:
cls.__bases__
:由類的基類組成的元組。cls.__qualname__
:其值是類或函數的限定名稱,即從模塊的全局做用域到類的點分路徑。cls.__subclasses__()
:這個方法返回一個列表,包含類的直接子類。其實現使用弱引用,防止超類和子類之間出現循環引用。這個方法返回的列表是內存裏現存的子類。cls.mro()
:構建類時,若是須要獲取儲存在類屬性__mro__ 中的超類元組,解釋器會調用這個方法。元類能夠覆蓋這個方法,定製要構建的類解析方法的順序。