Python 元类

Python 元类(metaclass)

对象

Python 中一切皆对象,连类也不例外。

对象中包含三个关键性属性:

  1. 标识 使用 id() 方法查看
  2. 对象值
  3. 类型 使用 type() 方法查看

类与对象

Python 中使用 class 关键字定义类,通过定义的类生成类实例。实例称之为实例对象。Python 中类是一个对象,所有内建类型的类型都是 type,这个 type 就是元类,元类就是 Python 中的造物主。

道生一,一生二,二生三,三生万物。

道 即是 type
一 即是 metaclass(元类,或者叫类生成器)
二 即是 class(类,或者叫实例生成器)
三 即是 instance(实例)
万物 即是 实例的各种属性与方法,我们平常使用python时,调用的就是它们。
type>>>metaclass>>>class>>>instance
^  |
|<<|
# class 关键词创建
class Base:
    counter = 10

class Derived(Base):
    def get_counter(self):
        return self.counter

x = Derived()
x.get_counter()
# 动态创建
Base = type('Base', (), {'counter': 10})
Derived = type('Derived', (Base,), dict(get_counter=lambda self: self.counter))

x = Derived()
x.get_counter()

# type 函数
type(name, bases, dict):

    name: 字符串类型存放新类的名字
    bases: 元组(tuple)类型指定类的基类/父类
    dict: 字典类型存放该类的所有属性(attributes)和方法(method)

现在我们都知道类(对象)可以使用 class 关键字创建,我们还知道类(对象)的类型是 type,既然知道了它的类型是 type,那么肯定可以通过 type(元类)来创建。

类的创建过程

  1. 确定类的父类是否包含 metaclass 参数,没有就执行下一步
  2. 确定类的父类的父类是否包含 metaclass 参数,没有就执行下一步
  3. 使用默认元类 type

定义元类

函数方式

def upper_attr(cls, base, attr):
    attrs = ((name, value) for name, value in attr.items() if not namw.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return type(cls, base, attr)

def Foo(object):
    __metaclass__ = upper_attr
    bar = 'bar'
print hasattr(Foo, 'bar')
# 输出: False
print hasattr(Foo, 'BAR')
# 输出:True

f = Foo()
print f.BAR
# 输出:'bar'

类方式

# 请记住,'type'实际上是一个类,就像'str'和'int'一样。所以,你可以从type继承
# __new__ 是在__init__之前被调用的特殊方法,__new__是用来创建对象并返回之的方法,__new_()是一个类方法
# 而__init__只是用来将传入的参数初始化给对象,它是在对象创建之后执行的方法。
# 你很少用到__new__,除非你希望能够控制对象的创建。这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情。还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用,下面我们可以单独的讨论这个使用

class UpperAttrMetaclass(type):
    def __new__(cls, name, bases, dct):
        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

元类的应用

  • ORM
  • 限定子类行为
  • 注册所有子类

实例化过程

实例:__call__ »> __new__ »> __init__ »> instance

类实例:__prepare__ »> __call__ »> __new__ »> __init__ »> class instance

特殊函数签名

>>> class Meta(type):
...     @classmethod
...     def __prepare__(mcs, name, bases, **kwargs):
...         print('  Meta.__prepare__(mcs=%s, name=%r, bases=%s, **%s)' % (
...             mcs, name, bases, kwargs
...         ))
...         return {}

...     def __new__(mcs, name, bases, attrs, **kwargs):
...         print('  Meta.__new__(mcs=%s, name=%r, bases=%s, attrs=[%s], **%s)' % (
...             mcs, name, bases, ', '.join(attrs), kwargs
...         ))
...         return super().__new__(mcs, name, bases, attrs)

...     def __init__(cls, name, bases, attrs, **kwargs):
...         print('  Meta.__init__(cls=%s, name=%r, bases=%s, attrs=[%s], **%s)' % (
...             cls, name, bases, ', '.join(attrs), kwargs
...         ))
...         return super().__init__(name, bases, attrs)

...     def __call__(cls, *args, **kwargs):
...         print('  Meta.__call__(cls=%s, args=%s, kwargs=%s)' % (
...             cls, args, kwargs
...         ))
...         return super().__call__(*args, **kwargs)
...

元类 super 方法

import random

class Meta(type):
    # 类变量
    _instance = None

    def __call__(cls, *args, **kwargs):
        print('meta: call: %s' % cls)
        if cls._instance is None:
            cls._instance = super(Meta, cls).__call__(*args, **kwargs)
        print("meta: call: returning %s@%s" % (cls._instance.__class__, id(cls._instance)))
        return cls._instance


class ClassA(object):
    __metaclass__ = Meta

    def __new__(cls, *args, **kwargs):
        print('classA: new')
        return super(ClassA, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        self.val = random.randint(1, 1000)
        print('classA: init')


class ClassB(object):
    __metaclass__ = Meta

    def __new__(cls, *args, **kwargs):
        print('classB: new')
        return super(ClassB, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('classB: init')
        self.val = random.randint(1, 1000)

class ClassC(ClassB):
    def __new__(cls, *args, **kwargs):
        print('classC: new')
        return super(ClassC, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print('classC: init')
        super(ClassC, self).__init__(self, *args, **kwargs)
        self.test = 3


def main():
    a1 = ClassA()
    b1 = ClassB()
    a2 = ClassA()
    b2 = ClassB()
    c1 = ClassC()

    print(a1.val)
    print(b1.val)
    print(a2.val)
    print(b2.val)

if __name__ == '__main__':
    main()

x = Foo() == type(Foo).__call__(Foo)

元类中的 super() == super(Singleton, cls) 方法调用了待实例化的类 __new__ and __init__

  • super(cls, instance) (isinstance(instance, cls) must be True), the method is selected from the next class in instance.__class__.__mro__ starting from instance.__class__.
  • super(cls0, cls1) (issubclass(cls1, cls0) must be True), the method is selected from next class in cls1.__mro__ starting from cls0

Reference