你想通过改变实例创建方式来实现单例、缓存或其他类似的特性。

类的创建过程

https://docs.python.org/3/reference/datamodel.html#metaclasses

class-creation

  1. 当 Python 见到 class 关键字时,会首先解析 class ... 中的内容。例如解析基类信息,最重要的是找到对应的元类信息(默认是 type)。
  2. 元类找到后,Python 需要准备 namespace (也可以认为是上节中 typedict 参数)。如果元类实现了 __prepare__ 函数,则会调用它来得到默认的 namespace 。
  3. 之后是调用 exec 来执行类的 body,包括属性和方法的定义,最后这些定义会被保存进 namespace。
  4. 上述步骤结束后,就得到了创建类需要的所有信息,这时 Python 会调用元类的构造函数来真正创建类。

如果你想在类的创建过程中做一些定制(customization)的话,创建过程中任何用到了元类的地方,我们都能通过覆盖元类的默认方法来实现定制。这也是元类“无所不能”的所在,它深深地嵌入了类的创建过程。

type动态创建类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 常规方法创建类
class Foo(object):
    name = "luenci"

    def func(self):
        return 666


# 基于type创建类
# - 类名
# - 继承的类
# - 类属性
# - 类方法

foo1 = type("Foo", (object,), {"name": "luenci", "func": lambda self: "hi"})

元类声明

 1
 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
class MateFoo(type):

    def __init__(self,*args,**kwargs):
        print("init")
        # super().__init__(*args, **kwargs)
        type.__init__(*args, **kwargs)

    def __new__(cls, *args, **kwargs):
        # 创建类
		# new_cls = super().__new__(cls, *args, **kwargs)
        new_cls = type.__new__(cls, *args, **kwargs)
        print("new ", new_cls)
        return new_cls

    def __call__(self, *args, **kwargs):
        # 1.调用自己类的 __new__ 方法去创建对象
        empty_obj = self.__new__(self)

        # 2.调用自己类的 __init__ 方法去初始化
        self.__init__(empty_obj, *args, **kwargs)
        print("call", empty_obj)

        return empty_obj

# Foo1 类就是MateFoo的对象
class Foo1(object, metaclass=MateFoo):
    def __init__(self, name):
        self.name = name

print(Foo1("hi"))

元类实现单例模式

 1
 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
class Singleton(type):
    """
    单例元类.
    """
    _instance = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 可以设置子类继承的字段
        self.author = "luenci"

    def __call__(self, *args, **kwargs):
        # 对象实例化的时候调用
        # 判断是否已经实例化对象
        if not self._instance:
            self._instance = self.__new__(self)
        self.__init__(self._instance, *args, **kwargs)

        return self._instance


class DBclient(object, metaclass=Singleton):
    """数据库单例连接."""

    def __init__(self, url, port, user, pwd):
        self.url = url
        self.port = port
        self.url = user
        self.pwd = pwd