python学习:metaclass超级类

metaclass,有人说是元类,有人说是超越变形类。总之就是站在高处的一个很厉害的东西。要了解metaclass,需要首先了解type的__call__函数。参考:https://www.jianshu.com/p/33d6688246a6
我们知道我们自定义的python类都是有type实例的重载__call__()函数创建的。如果有个东西,能够拦截python类的定义,不在有type实例创建,而变成我们自己定义的类创建呢,没错,那个东西就是metaclass。

metaclass 是 type 的子类,通过替换 type 的__call__运算符重载机制,“超越变形”正常的类。

我们创建一个metaclass类

class MyMetaClass(type):
    def __init__(cls, name, bases, kwds):
        super(MyMetaClass, cls).__init__(name, bases, kwds)
        print("meta class __init__")
        
type(MyMetaClass)
#输出
type

再定义一个简单的类,设置他的metaclass为MyMetaClass

class MyClass(metaclass = MyMetaClass):
    def __init__(self,data):
        self.data = data
        print("myclass init")
print("========")        
print(type(MyClass))

###### 输出 ######
meta class __init__
========
<class '__main__.MyMetaClass'>

从上面的例子中可以看到,在定义MyClass的时候,就已经执行了MyMetaClass的init方法了。而最终的MyClass的type也变成了MyMetaClass。

更加直观一点的,我们换成继承自MyMetaClass.

class MyClass(MyMetaClass):
    def __init__(self,data):
        self.data = data
        print("myclass init")
print("========")        
print(type(MyClass))

###### 输出 ######
========
<class 'type'>

如果只是简单的继承,定义的时候不会执行MyMetaClass的init方法,继承下来的类型也还是type.
上面的代码似乎与__call__没什么关系,下面我们看一个比较完整的例子,有个更清晰的认识:

#metaclass类
class MyMetaClass(type):
    def __init__(cls, name, bases, kwds):
        super(MyMetaClass, cls).__init__(name, bases, kwds)
        print("meta class __init__")
        print("init cls is:{},name is:{},bases is:{},kwds is:{}".format(cls,name,bases,kwds))
        print(cls.tag)
        
    def __new__(cls,*args,**kwargs):
        print("meta class __new__,new_cls:{}".format(cls))
        return type.__new__(cls,*args,**kwargs)
    
    def __call__(cls, *args, **kwargs):
        print('=meta class __call__,call_cls:{}====='.format(cls))
        obj = cls.__new__(cls)
        cls.__init__(cls, *args, **kwargs)
        return obj
#定义一个类,设置它的metaclass为MyMetaClass
class MyClass(metaclass = MyMetaClass):
    tag = 'tag'
    def __init__(self,data):
        self.data = data
        print("MyClass __init__")
        
    def __new__(cls, *args, **kwargs):
        print('MyClass.__new__')
        return object.__new__(cls)
print("========")        
print(type(MyClass))

#### 输出 #####
meta class __new__,new_cls:<class '__main__.MyMetaClass'>
meta class __init__
init cls is:<class '__main__.MyClass'>,name is:MyClass,bases is:(),kwds is:{'__module__': '__main__', '__qualname__': 'MyClass', 'tag': 'tag', '__init__': <function MyClass.__init__ at 0x7fb4b8725ee0>, '__new__': <function MyClass.__new__ at 0x7fb4b8725af0>}
tag
========
<class '__main__.MyMetaClass'>
#创建实例
myclass = MyClass(1)
print(myclass.data)
myclass = MyClass(2)
print(myclass.data)

#### 输出 #####
=meta class __call__,call_cls:<class '__main__.MyClass'>=====
MyClass.__new__
MyClass __init__
1
=meta class __call__,call_cls:<class '__main__.MyClass'>=====
MyClass.__new__
MyClass __init__
2

总结:
1、在定义类的时候,设置metaclass,则会先执行metaclass的__new__方法,返回的是一个metaclass对象。
2、执行完__new__之后,则调用metaclass的__init__方法,这时传入的cls为我们所定义的类。如果想对这个类在定义好之后(实例化前)做一些什么设置(如注册到某个全局变量中,则可在此进行操作)
3、在每次实例化类的时候,就会调用MetaClass的__call__()函数,而不是type的__call__()函数。从而实现了对python类定义的拦截

那么拦截对python类的定义有什么用处呢?metaclass与类装饰器、与继承比较呢?
首先,metaclass与类装饰器都与__call__()函数相关,类装饰器一般用于装饰函数,是装饰的函数变成类的一个(只一个)实例,使类的实例能像函数一样去调用。而metaclass用于类的设置,改变了类的定义,而且一个类可以创建多个实例
metaclass与继承比较,继承并不会改变类的定义,继承的类在定义时并不会有任何执行。继承不会改变类的创建,继承不会改变类的定义。
继承是两个类之间有一些共同的属性或方法,抽出这些共同的属性和方法使之成为父类。而设置metaclass的类之间并不一定要存在共同属性和方法。它主要用于在类定义完成之后,实例化之前,对类的操作。

metaclass改变了类的定义,创建,需要慎用。一般建议用于框架而非应用层面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,333评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,812评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,016评论 0 246
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,293评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,650评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,788评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,003评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,741评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,462评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,681评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,168评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,528评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,169评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,119评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,846评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,719评论 2 274

推荐阅读更多精彩内容