python的metaclass使用小結
python中的metaclass可謂熟悉而又陌生,自己開發(fā)時很少用,閱讀源碼時卻經常遇到,那么到底什么是metaclass呢?何時使用metaclass呢?
動態(tài)創(chuàng)建class的方法
假設我們需要動態(tài)創(chuàng)建一個class,那么一般我們有這樣幾種方法
- 通過一個函數動態(tài)創(chuàng)建class
- 通過type動態(tài)創(chuàng)建class
1.函數動態(tài)創(chuàng)建class
def create_class_by_name(name): if name == 'dog': class Dog(object): pass return Dog else: class Cat(object): pass return Cat dy_class = create_class_by_name('hi') print dy_class # output: <class '__main__.Cat'> print dy_class() # output: <__main__.Cat object at 0x03601D10>
2.type動態(tài)創(chuàng)建class
type 除了可以獲取到一個對象的類型,還有另外一個功能:動態(tài)創(chuàng)建 class。
它的函數簽名是這樣的:_type(name, bases, dict) -> a new type_
其中:
- name: 類名
- bases: 父類名的tuple,用于繼承,可以為空
- dict: 字典,包含class attributes的 name 和 value
class ClassParent(object): first_name = 'John' # 創(chuàng)建一個 繼承自 ClassParent 的 ClassChild,并為 ClassChild 添加一個 age = 15 的 attribute child_class = type('ClassChild', (ClassParent,), {'age': 15}) # child_class 形如: class ClassChild(ClassParent): age = 15 child_obj = child_class() print child_obj.first_name, child_obj.age # output: John 15
事實上,type 關鍵字,是 python 用來創(chuàng)建 class 的 metaclass??梢酝ㄟ^ __class__
來查看一個 class 的 metaclass:
print child_class.__class__ # output <type 'type'>
使用metaclass創(chuàng)建class
metaclass,即是(class of class) class 的 class,用來描述如何創(chuàng)建一個 class 的代碼段。
python2
在 class 的定義中,可以通過 __metaclass__
來指定當前 class 的 metaclass:
因此,只要我們指定了__metaclass__就可以代替type()創(chuàng)建class.我們自己來寫一個最簡單的metaclass.
class DemoMeta(type): pass class DemoClass(object): __metaclass__ = DemoMeta print type(DemoClass) #<class '__main__.DemoMeta'>
看一個復雜些的例子
class FooMeta(type): def __new__(mcs, name, bases, attrs): """ 定制創(chuàng)建 class 的行為 作為示例,這里將外部傳入的 attrs 的名稱做一些處理:如果以'_'開頭,則轉為小寫 :param name: class 名稱 :param bases: tuple, 父類的名稱 :param attrs: class attributes """ converted = {atr if not atr.startswith('_') else atr.lower(): v for atr, v in attrs.items()} cls = super(FooMeta, mcs).__new__(mcs, name, bases, converted) return cls class Foo(object): __metaclass__ = FooMeta
python3
py3中,指定元類的語法有一點小小的修改:不再使用 __metaclass__
,而是在定義 class 時顯式地指定 metaclass:
class Foo(object, metaclass=CustomMetaclass): pass
常見用途
metaclass可以控制類的創(chuàng)建過程,包括類的屬性、方法和父類等。metaclass可以用于實現(xiàn)一些高級的編程技巧,例如自動注冊子類、自動添加屬性和方法等
- 統(tǒng)計某種類型
- 定義一個單例
- 自動添加屬性和方法
如何統(tǒng)計某個類的所有子類#
猜想一下,統(tǒng)計某個類的所有子類
__bases__
是一個元組,包含了一個類的所有直接父類,所以不不能統(tǒng)計到某種類型
還有一種方法:
使用gc.get_objects()函數獲取所有已經創(chuàng)建的對象,然后使用issubclass()函數判斷一個類是否是另一個類的子類,從而統(tǒng)計所有的子類
以下是一個示例代碼:
import gc def count_subclasses(cls): count = 0 for obj in gc.get_objects(): if isinstance(obj, type) and issubclass(obj, cls): count += 1 return count
自動統(tǒng)計某種類型
下面是一個簡單的例子演示了如何使用metaclass來自動注冊子類。
假設我們有一個基類Base,我們希望所有繼承自Base的子類都能夠自動注冊到一個全局的字典中。我們可以定義一個Meta類,該類繼承自type,并重寫其__init__
方法,在該方法中實現(xiàn)自動注冊的邏輯。然后,我們將Base類的metaclass設置為Meta類,這樣所有繼承自Base的子類都會使用Meta類來創(chuàng)建實例,并自動注冊到全局字典中。
class Meta(type): registry = {} def __init__(cls, name, bases, attrs): super(Meta, cls).__init__(name, bases, attrs) if name != 'Base': Meta.registry[name] = cls class Base(object): __metaclass__ = Meta class Subclass1(Base): pass class Subclass2(Base): pass print Meta.registry
輸出結果為:
{'Subclass1': <class '__main__.Subclass1'>, 'Subclass2': <class '__main__.Subclass2'>}
可以看到,Subclass1和Subclass2都被自動注冊到了Meta.registry字典中。這樣,我們就可以方便地獲取所有繼承自Base的子類了。
定義單例
class Singleton(type): def __init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) cls.instance = None def __call__(cls, *args): if cls.instance is None: cls.instance = super(Singleton, cls).__call__(*args) return cls.instance class MyCard(object): __metaclass__ = Singleton def testSingle(): card1 = MyCard() card2= MyCard() print card1,card2 #輸出結果:<__main__.MyCard object at 0x03A6FE90> <__main__.MyCard object at 0x03A6FE90>
自動添加屬性和方法
假設我們有一個基類Base,我們希望所有繼承自Base的子類都能夠自動添加一個名為name的屬性和一個名為hello的方法。我們可以定義一個Meta類,該類繼承自type,并重寫其__init__
方法,在該方法中實現(xiàn)自動添加屬性和方法的邏輯。然后,我們將Base類的metaclass設置為Meta類,這樣所有繼承自Base的子類都會使用Meta類來創(chuàng)建實例,并自動添加name屬性和hello方法。
class Meta(type): def __init__(cls, name, bases, attrs): super(Meta, cls).__init__(name, bases, attrs) cls.name = name cls.hello = lambda self: 'Hello, %s!' % self.name class Base(object): __metaclass__ = Meta class Subclass1(Base): pass class Subclass2(Base): pass print Subclass1().hello() print Subclass2().hello() #Hello, Subclass1! #Hello, Subclass2!
Python選取 metaclass 的策略
在Python中,當我們定義一個類時,解釋器會根據以下順序來選擇metaclass:
- 如果該類顯式指定了metaclass,則使用該metaclass。
- 否則,如果該類的父類中有metaclass,則使用該metaclass。
- 否則,如果該類的模塊中有metaclass,則使用該metaclass。
- 否則,如果該類的基類中有metaclass,則使用該metaclass。
- 否則,使用默認的type作為metaclass。
結尾
如果看完之后你還是看不懂,沒關系,99%的情況下都不需要用到metaclass
到此這篇關于python的metaclass的文章就介紹到這了,更多相關python的metaclass內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python導入csv文件出現(xiàn)SyntaxError問題分析
這篇文章主要介紹了python導入csv文件出現(xiàn)SyntaxError問題分析,同時涉及python導入csv文件的三種方法,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12Django模板獲取field的verbose_name實例
這篇文章主要介紹了Django模板獲取field的verbose_name實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05matplotlib之pyplot模塊坐標軸標簽設置使用(xlabel()、ylabel())
這篇文章主要介紹了matplotlib之pyplot模塊坐標軸標簽設置使用(xlabel()、ylabel()),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02