# 元类的深度理解
# 代码解释
class A:
def __init__(self,name):
self.name = name
print('__init__方法')
def __call__(self, *args, **kwargs):
print('执行__call__方法')
def __new__(cls, *args, **kwargs):
cls.name = 'xxx'
print('执行__new__方法')
return object.__new__(cls)
# 给类传递参数,会触发类的先触发__new__、在触发__init__方法
a = A('sss')
print(a.name)
print(A.name)
print('---------对象触发__call__------------')
a()
# 执行__new__方法
# __init__方法
# sss
# xxx
# ---------------------
# 执行__call__方法
print('-------------继承元类-------------------')
class MyType(type):
def __init__(self,what,bases=None,dict=None):
print('Mytype的__init__方法')
super().__init__(what,bases,dict)
def __call__(self, *args, **kwargs): #3.触发__call__方法,在这个方法中执行自身的__init__方法
obj = self.__new__(self,*args,**kwargs)
print('Mytype的__call__方法')
self.__init__(obj,*args,**kwargs) #4.执行自身的init,进行初始化
return obj # 重点, 本身的self是在父类的__call__中调用了__init__函数后,赋值后,返回,不返回的话,obj返回的是None
def __new__(cls, name, bases, attrs):
print('MyType中的new方法') # 最先打印
return type.__new__(cls, name, bases, attrs)
class Foo(object,metaclass=MyType): #1.在指定元类的第一时间,回去执行Mytype中执行 它的__new__方法,再执行它的__init__方法
# __metaclass__ = MyType #py3中不能使用这种方法指定元类
def __init__(self,name):
print('Foo类中的__init__方法')
self.name = name
def __new__(cls, *args, **kwargs):
print('Foo中的__new__方法')
return object.__new__(cls)
def __call__(self):
print('Foo中的call方法')
obj = Foo('xx') #2.实例化Foo类,最先执行类得__new__方法,返回一个空间空间
print(obj.name)
obj()
# MyType中的new方法
# Mytype的__init__方法
# Foo中的__new__方法
# Mytype的__call__方法
# Foo类中的__init__方法
# xx
# Foo中的call方法
print('--------------一般继承----------------------')
class A:
def __call__(self, *args, **kwargs):
print('A的__call__方法')
class B(A):
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
print('B的__new__方法')
return object.__new__(cls)
b = B('xxx')
b()
# B() # 报错,只能实例化
a = A()
a() # 对应才能触发__call__方法,B()不能, A()也不能
# B的__new__方法
# A的__call__方法
# A的__call__方法
# Metaclass的使用
实例一:
class MyType(type): def __init__(self, *args, **kwargs): print('MyType创建类',self) super(MyType, self).__init__(*args, **kwargs) def __call__(self, *args, **kwargs): obj = super(MyType, self).__call__(*args, **kwargs) print('类创建对象', self, obj) return obj class Foo(object,metaclass=MyType): user = 'wupeiqi' age = 18 obj = Foo()
实例二
class MyType(type): def __init__(self, *args, **kwargs): super(MyType, self).__init__(*args, **kwargs) def __call__(cls, *args, **kwargs): v = dir(cls) obj = super(MyType, cls).__call__(*args, **kwargs) return obj class Foo(MyType('MyType', (object,), {})): user = 'wupeiqi' age = 18 obj = Foo()
实例三
class MyType(type): def __init__(self, *args, **kwargs): super(MyType, self).__init__(*args, **kwargs) def __call__(cls, *args, **kwargs): v = dir(cls) obj = super(MyType, cls).__call__(*args, **kwargs) return obj def with_metaclass(arg,base): return MyType('MyType', (base,), {}) class Foo(with_metaclass(MyType,object)): user = 'wupeiqi' age = 18 obj = Foo()
# 总结
1、执行顺序? 在实例化一个继承元类的类时,先在加载时就会触发继承的元类的__new__方法、__init__方法,在实例时,先触发类的__new__方法、在触发元类的__call__方法,在__call__方法中触发self.__init__方法,最后返回call中创建的对象 2、那么__new__在整个过程中扮演什么角色? __new__方法主要用来创建类空间以及类 3、在object的部分代码,使用c写的(CPython),也就是说源码不可见
# 元类剖析
在大多数语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成
立:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
但是在Python中类也是对象. 是的,对象. 每当你用到关键字 class , Python就会执行它并且建立一个对象.例如:
>>> class ObjectCreator(object):
... pass
...
上面代码在内存里创建了名叫"ObjectCreator"的对象. 这个对象(类)有生成对象(实例)的能力,这就是为什么叫做类. 它是个对象,所以:
1、你可以把它赋值给一个变量
2、你可以赋值它
3、你可以给它添加属性
4、你个以作为函数参数来传递它
>>> print(ObjectCreator) # 你可以打印一个类,因为它是一个对象
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # 你可以把类作为参数传递
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # 可以给一个类添加属性
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # 可以把类赋值给一个变量
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
# 动态创建类
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。 首先,你可以在函数中创建类,使用class关键字即可:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # 返回一个类不是一个实例
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # 返回一个类不是一个实例
<class '__main__.Foo'>
>>> print(MyClass()) # 你可以在类里创建一个对象
<__main__.Foo object at 0x89c6d4c>
但这还不够动态,因为你仍然需要自己编写整个类的代码. 既然类是对象,那么肯定有什么东西来生成它. 当你使用关键字 objects ,Python自动的创建对象.像Python中大多数的东西一样,他也给你自 己动手的机会. 记得函数 type 吗?这个古老好用的函数能让你知道对象的类型是什么:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
这里, type 有一种完全不同的能力,它也能动态的创建类. type 可以接受一个类的描述作为参数,然后返回一个类. (我知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)
# type(类名, 父类名的元组 (针对继承情况,可以为空), 包含属性的字典(名称和值))
>>> class MyShinyClass(object):
... pass
>>> MyShinyClass = type('MyShinyClass', (), {}) # 返回类对象
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # 创建一个类的实例
<__main__.MyShinyClass object at 0x8997cec>
type 可以接受一个字典来定义类的属性:
# 最终效果
>>> class Foo(object):
... bar = True
# type实现
>>> Foo = type('Foo', (), {'bar':True})
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True
# 继承最终效果
>>> class FooChild(Foo):
... pass
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar从Foo继承
True
要是在类中添加方法,你要做的就是把函数名写入字典就可以了,不懂可以看下面:
>>> def echo_bar(self):
... print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True
你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class 时Python在幕后做的事情,而这就是通过元类来实现的。
# 什么是元类**(终于到正题了)**
元类就是创建类的东西. 你是为了创建对象才定义类的,对吧? 但是我们已经知道了Python的类是对象. 这里,元类创建类.它们是类的类,你可以把它们想象成这样:
MyClass = MetaClass()
MyObject = MyClass()
你已经看到了 type 可以让你像这样做:
MyClass = type('MyClass', (), {})
这是因为 type 就是一个元类. type 是Python中创建所有类的元类. 现在你可能纳闷为啥子 type 用小写而不写成 Type ? Python中所有的东西都是对象.包括整数,字符串,函数还有类.所有这些都是对象.所有这些也都 是从类中创建的:
>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo():
pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object):
pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
那么, __class__ 的 __class__ 属性是什么?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
所以,元类就是创建类对象的东西. 如果你愿意你也可以把它叫做'类工厂'. type 是Python的内建元类,当然,你也可以创建你自己 的元类
# **__metaclass__**属性
当你创建一个函数的时候,你可以添加__metaclass__属性:
class Foo(object):
__metaclass__ = something
... [...]
如果你这么做了,Python就会用元类来创建类Foo. 小心点,这里面有些技巧. 你首先写下 class Foo(object ,但是类对象 Foo 还没有在内存中创建. Python将会在类定义中寻找 __metaclass__ .如果找打了就用它来创建类对象 Foo .如果没找到, 就会默认用 type 创建类. 把下面这段话反复读几次。
当你写如下代码时 :
class Foo(Bar): pass
Python将会这样运行:
在 Foo 中有没有 __metaclass__ 属性?
如果有,Python会在内存中通过 __metaclass__ 创建一个名字为 Foo 的类对象(我说的是类对
象,跟紧我的思路).
如果Python没有找到 __metaclass__ ,它会继续在Bar(父类)中寻找 __metaclass__ 属性 ,并
尝试做和前面同样的操作.
如果Python在任何父类中都找不到 __metaclass__ ,它就会在模块层次中去寻
找 __metaclass__ ,并尝试做同样的操作。
如果还是找不到 __metaclass__ ,Python就会用内置的 type 来创建这个类对象。
现在的问题就是,你可以在 __metaclass__ 中放置些什么代码呢?
答案就是:可以创建一个类的东西。
# 自定义元类
元类的主要目的就是为了当创建类时能够自动地改变类.
通常,你会为API做这样的事情,你希望可以创建符合当前上下文的类.
假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方
法可以办到,但其中一种就是通过在模块级别设定 __metaclass__ .
采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有
的属性都改成大写形式就万事大吉了。
幸运的是,__metaclass__ 实际上可以被任意调用,它并不需要是一个正式的类(我知道,某
些名字里带有'class'的东西并不需要是一个class,画画图理解下,这很有帮助)。
所以,我们这里就先以一个简单的函数作为例子开始。
# 元类会自动将你通常传给'type'的参数作为自己的参数传入
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""返回一个将属性列表变为大写字母的类对象 """
# 选取所有不以'__'开头的属性,并把它们编程大写
uppercase_attr = {}
print('xx')
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[
name] = val # 用'type'创建类
return type(future_class_name, future_class_parents, uppercase_attr)
class Foo(object, metaclass=upper_attr): # global __metaclass__ won't work with "object" though
# 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
bar = 'bip'
print(hasattr(Foo, 'bar')) # 输出: False
print(hasattr(Foo, 'BAR')) # 输出: True
f = Foo()
print(f.BAR) # 输出: 'bip'
← Python内置方法 函数 →