# 元类的深度理解

# 代码解释

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'