 元类
元类
  阅读此篇博文的顺序: 类定义阶段 - 类的实例化 - 元类 - 示例(源码)  , 剩下的作为补充. 这样可以更顺畅的理解元类.
 因为 type和object 、__call__ 这两部分的内容自己当时总结的有些许生涩难懂..复习时也想了好一会.. 不是特别重要,了解即可!”
别较真! 深究就涉及C语言结构体了.  在此阶段, 我们只需要明白:
object是所有类的基类, 其他所有的类都是由type创建的. 想要自定义创建, 得基于metaclass指定一个类, 该类继承type!!
元类是做什么的? 它是用来控制我们类的生成过程的, 默认情况下, 我们自定义的类都是由type创建的.
# 类定义阶段
定义类 类似于 `import 模块名`
导入模块会创建一个namescope. 通过 模块名.属性名 从namescope中取属性/变量.
同理! 从上往下运行到class定义的类代码,会立刻开辟一个类的namescope,将类中的变量和方法/函数往namescope中丢.
★ (类中的代码/类体代码 在定义阶段就执行啦!!)
★ 记住: "执行 py文件、执行导入模块、类定义、类的实例化/类的调用、函数调用 的代码时,会开辟namescope."
       特别注意,类中函数体的代码要在函数被调用时才会执行!
        ---------
       | 数据属性 |
类名 -->|        |
       | 函数属性 |
        ---------  类名指向类的namescope
查看类的namescope: 类名.__dict__
类名.属性名 等同于 类名.__dict__["属性名"]
★ So,对namescope的CURD的本质就是在操作字典!! 
★ (类的实例是一样的! 查看实例的namescope: 实例名.__dict__)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 类的实例化
类在实例化的时候会自动调用
__init__, 但其实在调用__init__之前会先自动调用__new__!
# 基本使用
__new__: 为类实例化对象申请'开辟'一片内存 / 创建实例对象的;
__init__: 为类实例化对象设置独有的属性 / 为实例对象绑定属性;
class A:
  	# ★!!注意:该方法就是一个普通方法 print(A.__new__) --> <function A.__new__ at 0x7f9b6ba068b0>
    #        只不过会类实例化时会自动调用,并将当前类作为cls的实参传入.
    def __new__(cls, *args, **kwargs): 
        print("__new__")
        # !!!这里的参数cls就表示A这个类本身!!! ★ 若class B(A):pass,那么B()时,此处的cls就是<class '__main__.B'>
        print(cls)  # <class '__main__.A'>
        # object.__new__(cls) 便是根据cls创建cls的实例对象
        return object.__new__(cls)
    # 然后执行__init__, 里面的self指的就是实例对象
    # 在执行__init__的时候, __new__的返回值会自动作为参数传递给这里的self
    def __init__(self, *args, **kwargs):
        print("__init__")
A()  
"""
__new__
<class '__main__.A'>
__init__
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 重要结论
★ 几个比较重要的结论!
1> **__new__中必须将类A的实例对象返回, 才会执行__init__, 并且执行的时候会自动将__new__的返回值作为参数传给self. **
2> 一个对象是什么, 取决于其类型对象的__new__返回了什么.
3>__new__里面的参数一定要和__init__是匹配的, 除了第一个参数之外.
参考文档: https://www.cnblogs.com/traditional/p/13593927.html 很佩服这位老哥,写得很棒!!
下面将用三段代码验证上述的三个结论!!
结论1的验证
class A:
    def __new__(cls, *args, **kwargs):
        print("__new__")
    def __init__(self):
        print("__init__")
A()  # __new__ 
# -- 我们看到只有__new__被调用了,__init__则没有!  【结论1的验证】
2
3
4
5
6
7
8
9
10
11
结论1和结论2的验证
class A:
    def __new__(cls, *args, **kwargs):
        print("__new__")
        # -- 这里必须返回A的实例对象, 否则__init__函数是不会执行的  【结论1的验证】
        return 123
    def __init__(self):
        print("__init__")
a = A()
print(a + 1)  
"""
__new__
124
"""
# -- 我们看到A在实例化之后得到的是一个整型, 原因就是__new__返回了123  【结论2的验证】
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
结论3的验证.
 object.__new__(cls)、__new__接收的name、__new__接收的age 会组合起来, 分别传给__init__的 self、name、age!!!
class A:
  	# __new__里面的参数一定要和__init__是匹配的, 除了第一个参数之外  【结论3的验证】
    def __new__(cls, name, age):
        return object.__new__(cls)
    def __init__(self, name, age):
        self.name = name
        self.age = age
# A("夏色祭", -1) 这里传入了两个参数, 那么: A、"夏色祭"、-1 就会组合起来, 分别传给__new__的 cls、name、age
# 然后__new__里面返回了一个实例对象
# ★★ 那么:
#    object.__new__(cls)创建的实例、__new__接收的name、__new__接收的age 会组合起来, 分别传给__init__的 self、name、age
a = A("夏色祭", -1)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# type和object
object是父子关系的顶端, 所有的数据类型的mro继承链的 继承关系 最后都是object; (type也会继承object)
type是类型 实例关系 的顶端. (object也是type的一个实例)
object提供基础的功能, type用于创建“类”. “type”由谁创建的呢? 别纠结了, 再深究就是C语言的结构体啦!!在python语法层面是解释不了的.
# 专业术语
首先我们这里把python中的对象分为三种:
1> 内建对象: Python中的内建对象、或者叫内置对象, 比如int、str、list、type、object等等;
2> class对象: 程序员通过Python中的class关键字定义的类. 当然我们往往也会把内建对象和class对象统称为类对象;
3> 实例对象: 由类对象(内建对象或者class对象)创建的实例.
而对象之间存在着以下两种关系:
1> is-kind-of: 对应面向对象理论中父类和子类之间的关系; -- 继承关系.
2> is-instance-of: 对应面向对象理论中类和实例之间的关系; -- 实例关系.
# 内置方法
继承关系
 __bases__ : 查看一个 类型/类 的所有父类;
 __base__ : 查看一个 类型/类 的继承的第一个类;
 issubclass(sub, super) 检查sub类是否是 super类的 子类, 是, 返回True.
注意: 在python3中统一了类与类型的概念, 类就是类型.. (详见: python面向对象/0_OOP基本/类就是类型 部分的笔记)
实例关系
 __class__ : 查看一个实例的类型;
 type : 也可以查看一个实例的类型; (即该实例是由谁实例化得到的)
 isinstance(obj,cls) : 返回True或False. 若想返回True,满足下面两个条件其中一个即可.
1> 检查obj是否是 类cls或类cls的子类 的一个实例, 是, 返回True;
2> type(obj)的类型 是否跟 cls 一致, 是, 返回True.
 这里的obj可以是类对象、实例对象.
class A(object):
    pass
class B(A):
    pass
if __name__ == '__main__':
    print(type(B))  # <class 'type'>
    print(B.mro())  # [<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    
    print(B.__base__)   # <class '__main__.A'>
    print(B.__bases__)  # (<class '__main__.A'>,)
    print(issubclass(B, object))  # True
    print(issubclass(B, type))    # False
    
    # 细品!!
    # isinstance(obj,cls) 检查obj是否是 <类cls或类cls的子类> 的一个实例, 是, 返回True;
    # 这里的obj可以是类对象、实例对象.
    # - obj是实例对象
    b = B()
    print(isinstance(b, B))       # True
    print(isinstance(b, A))       # True
    print(isinstance(b, object))  # True
    print(isinstance(b, type))    # False
    # - obj是类对象
    print(isinstance(B, type))    # True -- 类默认都是type实例化得到的
    print(isinstance(B, object))  # True -- 因为type的父类是object
    print(isinstance(B, A))       # False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 验证关系
type实例关系的老大, object继承关系的老大!!

实线表示继承关系 该类父类是谁/该类是谁的子类; 虚线表示实例关系 该对象是通过谁实例化得到的/该对象是什么类型..
 (类就是类型, python处处皆对象 So, 图中的<type 'list'> 既表示它是list类型,也表示它是list类.. 当然它也是一个对象! )
虚线是跨列产生关系, 而实线只能在一列内产生关系. 除了type和object两者外.
验证上图中所画的关系:
>>> object
<class 'object'>
>>> type
<class 'type'>
>>> object.__bases__  # object 无父类,因为它是链条顶端
()
>>> type.__bases__    # type是object的子类
(<class 'object'>,)
>>> type(object)      # object的类型是type
<class 'type'>
>>> type(type)        # type的类型是自己
<class 'type'>
# -- list, dict, tuple 这些内置数据类型,他们的父类都是object, 类型都是type/都是由type实例化得到的.
>>> list.__bases__
(<class 'object'>,)
>>> list.__class__
<class 'type'>
# -- list实例化得到了mylist对象,该对象类型是<type 'list'>, 但该对象没有父类
>>> mylist = [1,2,3]
>>> mylist.__bases__
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    mylist.__bases__
AttributeError: 'list' object has no attribute '__bases__'
>>> mylist.__class__
<class 'list'>
# -- 自定义的类
>>> c = C()
>>> C.__bases__
(<class 'object'>,)
>>> C.__class__
<class 'type'>
>>> c.__bases__
Traceback (most recent call last):
  File "<pyshell#17>", line 1, in <module>
    c.__bases__
AttributeError: 'C' object has no attribute '__bases__'
>>> c.__class__
<class '__main__.C'>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# __call__
 一个对象能否被调用, 取决于它的类型对象中是否定义了
__call__函数
# 基本使用
若想让类实例化对象可以被调用, 则需要在类中定义
__call__方法!
class Foo:
    def __call__(self, *args, **kwargs):
        print(self)    # <__main__.Foo object at 0x7f99cc986760>
        print(args)    # (1, 2, 3)
        print(kwargs)  # {'x': 4, 'y': 5}
        return "Hello"
print(Foo)  # <class '__main__.Foo'>
      
obj = Foo()
print(obj)  # <__main__.Foo object at 0x7f99cc986760>
# -- 要想让obj这个类实例化对象变成一个可调用的对象,需要在该对象的类中定义一个__call__方法
#    该实例方法会在调用实例化对象obj时自动触发,调用obj的返回值就是__call__方法的返回值
print(obj(1, 2, 3, x=4, y=5))  # Hello  --  obj的调用会触发Foo中__call__方法的执行
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 深入探究
>>> a = 1
>>> a()
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    a()
TypeError: 'int' object is not callable
  
>>> print(hasattr(int, "__call__"))
True
2
3
4
5
6
7
8
9
上述代码运行结果报错, 整数对象是不可调用的,这显然意味着int这个类里面没有__call__函数.
但我们通过反射打印 print(hasattr(int, "__call__")) 的结果为True.. 这是为何?
因为hasattr反射和类属性查找的规则是一样的.
 So, int的类型是type, 而type里面有__call__, 因此即便int里面没有,hasattr(int, "__call__")依旧是True
内心OS: 以往我们的记忆都是, hasattr、类属性、实例属性的查找都是基于继承链的, 大体没错, 这里做一点补充!!
class A:
    foo = "in A"
class B(A):
    temp = "in B"
if __name__ == '__main__':
    # ▲ 类属性查找顺序/查找类是否具有某个属性:
    #   类-父类-...-顶级父类object- 若继承链中没有,还会自动到对应的类型对象(即谁实例化出的该类,此处是type)里面去找
    print(hasattr(B, "temp"))
    print(hasattr(B, "foo"))
    print(hasattr(B, "__call__"))
    print(hasattr(type, "__call__"))
    # ▲ 实例属性查找顺序: 
    #   自身-类-父类-...-顶级父类object - 若继承链中没有,就真没有了.
    # ps: object内部没有__call__函数
    print(hasattr(B(), "temp"))
    print(hasattr(B(), "foo"))
    print(hasattr(B(), "__call__"))  # ★★★ 继承链中没有,就不会去元类中找啦!!
    
"""
True
True
True
True
True
True
False
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
一丢丢题外话.
所有的类都是可以调用的,因为type是它们的类型对象,而type内部是有__call__函数的.
但是默认情况下实例对象是不可调用的. 
实例对象加括号执行时,会去找__call__方法!
如果实例对象的类型对象、以及该类型对象所继承的类中没有定义__call__函数的话,会继续沿着继承链挨个进行搜索
直到搜索到object时发现还没有__call__函数的话,那么就报错了.
所以一个整数对象是不可调用的,而整数类型是可调用的,我们发现这并不是在编译的时候就能够检测出来的错误,而是在运行时才能检测出来!
2
3
4
5
6
7
# 元类
我们一步步由浅到深的分析.. 只分析drf序列器源码中所需要用到的知识点.
# 创建类
- type可以创建类.默认. class type: def __new__(): 创建类/开辟一个类的空间 def __init__(): 给创建的类进行初始化/往类空间放一些东西 # ★ 类加括号实例化,会先调用类中的new,再调用init Foo = type("Foo", (object,), {'v1': 123, 'func': lambda self: 999}) - 自定义类来创建类,但该自定义类需继承type类.因为得将type类的所有功能拿过来,在它基础上进行扩展定制. class MyType(type): # 重写了type类中的__new__方法.会优先调用这里的new,有很多扩展空间,此处该方法最后用了super. def __new__(): super().__new__()1
2
3
4
5
6
7
8
9
10
11
12
13
创建类: 方式一 通过class关键字 "本质上就是通过type创建的"
class Foo(object):
    v1 = 123
    def func(self):
        return 999
2
3
4
5
创建类: 方式二 通过type类 等同于 方式一
# type要么接收一个参数,要么接收三个参数.接收一个参数查看类型,接受三个参数创建一个类.
# 类名 = type("类名",(父类,),{成员})
Foo = type("Foo", (object,), {'v1': 123, 'func': lambda self: 999})
print(Foo)            # <class '__main__.Foo'>
print(Foo.__name__)   # Foo
print(Foo.__bases__)  # (<class 'object'>,)
print(Foo.v1)         # 123
2
3
4
5
6
7
基于自定义类MyType创建类: 方式一
class MyType(type):
    def __new__(mcs, *args, **kwargs):
        xx = super().__new__(mcs, *args, **kwargs)
        return xx  # <class '__main__.Foo'>  -- xx是创建的类
      
Foo = MyType("Foo", (object,), {'v1': 123, 'func': lambda self: 999})
2
3
4
5
6
基于自定义类MyType创建类: 方式二
class MyType(type):
    def __new__(mcs, *args, **kwargs):
        """
        mcs     <class '__main__.MyType'>
        args    ('Foo', (<class 'object'>,), {'v1': 123, 'func': <function <lambda> at 0x7fdcd35bb310>})
        kwargs  {}
        """
        xx = super().__new__(mcs, *args, **kwargs)
        return xx
class Foo(object, metaclass=MyType):
    v1 = 123
    def func(self):
        return 999
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 参数和返回值
元类和类之间的关系 和 类与实例对象的关系, 之间是很相似的, 完全可以把类对象看成是元类的实例对象.
Foo既然指定了metaclass为MyType, 表示Foo这个类由MyType创建, 那么MyType的__new__函数返回了什么, Foo就是什么!
class MyType(type):
    """
		我们看到一个类在创建的时候会向元类的__new__中传递三个值
		分别是类名、继承的基类、类的属性
    """
    def __new__(mcs, names, bases, attrs):
        print("mcs:", mcs)  # mcs: <class '__main__.MyType'> 当前类MyType!!
        print("names:", names)
        print("bases:", bases)
        print("attrs:", attrs)
"""
Foo指定metaclass, 表示Foo这个类由MyType创建
在前面,我们说类实例化过程中,__new__是为实例对象开辟内存的,那么MyType的实例对象是谁呢? 显然就是这里的Foo
简而言之, 因为Foo指定了metaclass为MyType, 所以Foo的类型就是MyType
但在这个例子中,Foo并没有被创建出来!!为何?
在前面我们已经说了,__new__一定要将创建的实例对象返回才可以,这里的MyType是元类
类对象Foo等于MyType的实例对象, MyType的__new__就负责为类对象Foo分配空间
但显然我们这里并没有分配, 而且返回的还是一个None, 如果我们返回的是123, 那么print(Foo)就是123
因为元类MyType里的__new__方法返回的值是None,So,Foo的类型是<class 'NoneType'>
"""
class Foo(object, metaclass=MyType):  # -- 指定元类MyType来创建Foo这个类
    pass
if __name__ == '__main__':
    print("===")
    print(Foo)
    print(type(Foo))
"""
mcs: <class '__main__.MyType'>
names: Foo
bases: (<class 'object'>,)
attrs: {'__module__': '__main__', '__qualname__': 'Foo'}
===
None
<class 'NoneType'>
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
创建类之前,可对类中的成员做点操作!!
class MyType(type):
    def __new__(mcs, names, bases, attrs):
        # ★ 创建类之前,可对类中的成员做点操作
        del attrs['v1']
        attrs['say'] = "Hello"
        # 等价于return type.__new__(MyType, names, bases, attrs) 表示类由MyType创建
        return super().__new__(mcs, names, bases, attrs)  # 创建类
        """注意:
        此处不能写成 type(name, bases, attr), 因为这样的话类还是由type创建的
        这样想就解释通了(´▽`)
        type(name, bases, attr) 等价于 type.__new__(type, name, bases, attr)
        """
class Foo(object, metaclass=MyType):
    v1 = 123
    def func(self):
        return 999
if __name__ == '__main__':
    print("===")
    for item in ["v1", "say"]:
        if hasattr(Foo, item):
            print(item, ":>>", getattr(Foo, item))
"""
===
say :>> Hello
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 继承情况
一个类在没有指定的metaclass的时候, 如果它的父类指定了, 那么这个类的metaclass等于父类的metaclass!!
class MyType(type):
    def __new__(mcs, name, bases, attr):
        name = name * 2
        return super().__new__(mcs, name, bases, attr)
class B(metaclass=MyType):
    pass
class A(B):
    pass
print(A.__class__)  # <class '__main__.MyType'> 等同于 type(A)
print(A.__name__)  # AA
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 扩展
上面阐述的有关元类的知识点, 用于看drf序列器的源码已经足够了.. 在这里, 再做一点元类知识的扩展.
class MyType(type):
    def __new__(mcs, name, bases, attrs):
        print("创建类.")
        return super().__new__(mcs, name, bases, attrs)
class Base(object, metaclass=MyType):
    def __new__(cls, *args, **kwargs):
        print("创建类实例化对象.")
        return object.__new__(cls)
    def __init__(self, *args, **kwargs):
        print("类实例化对象初始化.")
if __name__ == '__main__':
    print("===")
    obj = Base()
    
"""
创建类.
===
创建类实例化对象.
类实例化对象初始化.
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上述代码中 Base类实例化时,先new后init, 是如何触发的呢? 是由MyType中的call方法来触发的!!
 ★★★下面的程序能理清, 那元类就可以暂时毕业啦!! (⁎⁍̴̛ᴗ⁍̴̛⁎)
★注意思考清楚下述代码中的mcs、cls是啥! + 前面类的实例化小节那三条很重要的结论!!
class MyType(type):
    def __new__(mcs, name, bases, attrs):  # mcs --:> <class '__main__.MyType'>
        print("创建类.")
        return super().__new__(mcs, name, bases, attrs)
    def __call__(cls, *args, **kwargs):    # cls --:> <class '__main__.Base'>
        # 结合类的实例化小节那三条很重要的结论的验证示例,就能很快明白此处的__call__方法为何要这样写!!
        print("执行元类MyType的call方法.")
        obj = cls.__new__(cls, *args, **kwargs)
        print("---")
        cls.__init__(obj, *args, **kwargs)
        return obj
class Base(object, metaclass=MyType):
    def __new__(cls, *args, **kwargs):     # cls --:> <class '__main__.Base'>
        print("创建类实例化对象.")
        return object.__new__(cls)
    def __init__(self, *args, **kwargs):
        print("类实例化对象初始化.")
    def __call__(self, *args, **kwargs):
        return "Base.call"
if __name__ == '__main__':
    print("===")
    obj = Base()
    print(obj())
    
"""
创建类.
===
执行元类MyType的call方法.
创建类实例化对象.
---
类实例化对象初始化.
Base.call
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Q: 思考个问题.为啥会触发元类MyType中的call方法呢?
 A: 你这样想:
     如果我说,类实例化得到的对象加括号会触发类中的call方法,相当于 类()(), 没问题吧?! 嗯.这是call方法的常识.
     python处处皆对象.
     代入元类中,MyType()类实例化得到 Base类这个对象, Base这个对象再加括号. 相当于 MyType()()
     So, Base() 会触发MyType类中的call方法!!
     当然Base类实例化创建的对象加括号就会触发Base类里的call方法. ok, 这个解释闭环啦.
# 示例(源码)
1> UserSerializer类根据继承关系是由元类SerializerMetaclass创建的.
2> list(attrs.items()) 是浅拷贝, 之所以拷贝, 是因为对迭代对象循环的同时剔除迭代对象里面的元素,会遗漏一些元素!!
class SerializerMetaclass(type):
    def __new__(mcs, name, bases, attrs):
        # 在创建类之前做了一点小操作,细品. -- attrs是name中的成员,此处的name是UserSerializer.
        data_dict = {}
        for k, v in list(attrs.items()):
            if isinstance(v, int):
                data_dict[k] = attrs.pop(k)
        attrs['_declared_fields'] = data_dict
        return super().__new__(mcs, name, bases, attrs)
class BaseSerializer(object):
    pass
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    pass
class ModelSerializer(Serializer):
    pass
class UserSerializer(ModelSerializer):
    v1 = 111
    v2 = 222
    v3 = "world"
if __name__ == '__main__':
    print(UserSerializer._declared_fields)  # {'v1': 111, 'v2': 222}
    print(UserSerializer.v3)  # world
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
