python的metaclass使用小結(jié)
python中的metaclass可謂熟悉而又陌生,自己開發(fā)時(shí)很少用,閱讀源碼時(shí)卻經(jīng)常遇到,那么到底什么是metaclass呢?何時(shí)使用metaclass呢?
動(dòng)態(tài)創(chuàng)建class的方法
假設(shè)我們需要?jiǎng)討B(tài)創(chuàng)建一個(gè)class,那么一般我們有這樣幾種方法
- 通過一個(gè)函數(shù)動(dòng)態(tài)創(chuàng)建class
- 通過type動(dòng)態(tài)創(chuàng)建class
1.函數(shù)動(dòng)態(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動(dòng)態(tài)創(chuàng)建class
type 除了可以獲取到一個(gè)對(duì)象的類型,還有另外一個(gè)功能:動(dòng)態(tài)創(chuàng)建 class。
它的函數(shù)簽名是這樣的:_type(name, bases, dict) -> a new type_其中:
- name: 類名
- bases: 父類名的tuple,用于繼承,可以為空
- dict: 字典,包含class attributes的 name 和 value
class ClassParent(object):
first_name = 'John'
# 創(chuàng)建一個(gè) 繼承自 ClassParent 的 ClassChild,并為 ClassChild 添加一個(gè) 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事實(shí)上,type 關(guān)鍵字,是 python 用來創(chuàng)建 class 的 metaclass??梢酝ㄟ^ __class__ 來查看一個(gè) class 的 metaclass:
print child_class.__class__ # output <type 'type'>
使用metaclass創(chuàng)建class
metaclass,即是(class of class) class 的 class,用來描述如何創(chuàng)建一個(gè) class 的代碼段。
python2
在 class 的定義中,可以通過 __metaclass__ 來指定當(dāng)前 class 的 metaclass:
因此,只要我們指定了__metaclass__就可以代替type()創(chuàng)建class.我們自己來寫一個(gè)最簡(jiǎn)單的metaclass.
class DemoMeta(type): pass class DemoClass(object): __metaclass__ = DemoMeta print type(DemoClass) #<class '__main__.DemoMeta'>
看一個(gè)復(fù)雜些的例子
class FooMeta(type):
def __new__(mcs, name, bases, attrs):
"""
定制創(chuàng)建 class 的行為
作為示例,這里將外部傳入的 attrs 的名稱做一些處理:如果以'_'開頭,則轉(zhuǎn)為小寫
: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__ = FooMetapython3
py3中,指定元類的語法有一點(diǎn)小小的修改:不再使用 __metaclass__,而是在定義 class 時(shí)顯式地指定 metaclass:
class Foo(object, metaclass=CustomMetaclass):
pass常見用途
metaclass可以控制類的創(chuàng)建過程,包括類的屬性、方法和父類等。metaclass可以用于實(shí)現(xiàn)一些高級(jí)的編程技巧,例如自動(dòng)注冊(cè)子類、自動(dòng)添加屬性和方法等
- 統(tǒng)計(jì)某種類型
- 定義一個(gè)單例
- 自動(dòng)添加屬性和方法
如何統(tǒng)計(jì)某個(gè)類的所有子類#
猜想一下,統(tǒng)計(jì)某個(gè)類的所有子類
__bases__是一個(gè)元組,包含了一個(gè)類的所有直接父類,所以不不能統(tǒng)計(jì)到某種類型
還有一種方法:
使用gc.get_objects()函數(shù)獲取所有已經(jīng)創(chuàng)建的對(duì)象,然后使用issubclass()函數(shù)判斷一個(gè)類是否是另一個(gè)類的子類,從而統(tǒng)計(jì)所有的子類
以下是一個(gè)示例代碼:
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自動(dòng)統(tǒng)計(jì)某種類型
下面是一個(gè)簡(jiǎn)單的例子演示了如何使用metaclass來自動(dòng)注冊(cè)子類。
假設(shè)我們有一個(gè)基類Base,我們希望所有繼承自Base的子類都能夠自動(dòng)注冊(cè)到一個(gè)全局的字典中。我們可以定義一個(gè)Meta類,該類繼承自type,并重寫其__init__方法,在該方法中實(shí)現(xiàn)自動(dòng)注冊(cè)的邏輯。然后,我們將Base類的metaclass設(shè)置為Meta類,這樣所有繼承自Base的子類都會(huì)使用Meta類來創(chuàng)建實(shí)例,并自動(dòng)注冊(cè)到全局字典中。
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輸出結(jié)果為:
{'Subclass1': <class '__main__.Subclass1'>, 'Subclass2': <class '__main__.Subclass2'>}
可以看到,Subclass1和Subclass2都被自動(dòng)注冊(cè)到了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
#輸出結(jié)果:<__main__.MyCard object at 0x03A6FE90> <__main__.MyCard object at 0x03A6FE90>自動(dòng)添加屬性和方法
假設(shè)我們有一個(gè)基類Base,我們希望所有繼承自Base的子類都能夠自動(dòng)添加一個(gè)名為name的屬性和一個(gè)名為hello的方法。我們可以定義一個(gè)Meta類,該類繼承自type,并重寫其__init__方法,在該方法中實(shí)現(xiàn)自動(dòng)添加屬性和方法的邏輯。然后,我們將Base類的metaclass設(shè)置為Meta類,這樣所有繼承自Base的子類都會(huì)使用Meta類來創(chuàng)建實(shí)例,并自動(dò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中,當(dāng)我們定義一個(gè)類時(shí),解釋器會(huì)根據(jù)以下順序來選擇metaclass:
- 如果該類顯式指定了metaclass,則使用該metaclass。
- 否則,如果該類的父類中有metaclass,則使用該metaclass。
- 否則,如果該類的模塊中有metaclass,則使用該metaclass。
- 否則,如果該類的基類中有metaclass,則使用該metaclass。
- 否則,使用默認(rèn)的type作為metaclass。
結(jié)尾
如果看完之后你還是看不懂,沒關(guān)系,99%的情況下都不需要用到metaclass
到此這篇關(guān)于python的metaclass的文章就介紹到這了,更多相關(guān)python的metaclass內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實(shí)現(xiàn)修改文件創(chuàng)建時(shí)間(支持任意時(shí)間修改)
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)修改文件創(chuàng)建時(shí)間,可以支持任意時(shí)間修改,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-07-07
Python 項(xiàng)目轉(zhuǎn)化為so文件實(shí)例
今天小編就為大家分享一篇Python 項(xiàng)目轉(zhuǎn)化為so文件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-12-12
Pytorch使用MNIST數(shù)據(jù)集實(shí)現(xiàn)CGAN和生成指定的數(shù)字方式
今天小編就為大家分享一篇Pytorch使用MNIST數(shù)據(jù)集實(shí)現(xiàn)CGAN和生成指定的數(shù)字方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01
Ubuntu16.04/樹莓派Python3+opencv配置教程(分享)
下面小編就為大家分享一篇Ubuntu16.04/樹莓派Python3+opencv配置教程。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-04-04
Python?requests下載文件的幾種常用方法(附代碼)
這篇文章主要介紹了五種下載方式的實(shí)現(xiàn)方法,包括基礎(chǔ)下載、大文件分塊下載、帶有斷點(diǎn)續(xù)傳的下載、帶有超時(shí)和重試的下載以及完整的下載器實(shí)現(xiàn),文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2025-03-03
python使用MkDocs自動(dòng)生成文檔的操作方法
python代碼注釋風(fēng)格有很多,比較主流的有 reStructuredText風(fēng)格、numpy風(fēng)格、Google風(fēng)格,自動(dòng)生成文檔的工具也有很多,常見的有:Pydocs,Sphinx和MkDocs,本文給大家介紹了python使用MkDocs自動(dòng)生成文檔的操作方法,需要的朋友可以參考下2024-06-06

