首页 > 技术文章 > 面向对象之元类

featherwit 2020-07-19 17:06 原文

在Python中, 一切皆对象,  对象是类的实例化。那么类又是由什么实例化得到的呢?

元类的介绍

class People(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s:%s' % (self.name, self.age))
obj = People('featherwit', 18)

print(type(obj))  # <class 'People'>
print(type(People))  # <class 'type'>

如果一切皆为对象,那么类本质也是一个对象,既然所有的对象都是调用类得到的,那么必然也是调用了一个类得到的,这个类称为元类。

所以调用关系为:

 

class关键字创建类的流程

我们用class关键字定义的所有的类以及内置的类都是由元类type实例化得到的

class关键字创建类的流程主要步骤为:

1. 拿到类名: class_name = "People"
2. 拿到基类: class_bases = (object, )
3. 执行类体代码拿到类的名称空间: class_dic = {...}
4. 调用元类: People = type(class_name, class_bases, class_dic)

exec的用法:

exec:三个参数

参数一:包含一系列python代码的字符串

参数二:全局作用域(字典形式),如果不指定,默认为globals()

参数三:局部作用域(字典形式),如果不指定,默认为locals()

#可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中

 

自定义元类控制类的创建

一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类

class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    pass

class People(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s:%s' % (self.name, self.age))

自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即

People = Mymeta(People, (object, ), {...})

# 步骤:
# 1. 先造一个空对象 -> People, 调用类内的__new__方法
# 2. 调用Mymeta这个类内的__init__方法, 完成初始化操作
# 3. 返回初始化好的对象 -> 调用元类的__call__方法
class Mymeta(type):  # 只有继承了type的类才是元类

    def __init__(self, class_name, class_bases, class_dic):
        # print(self)  # <class '__main__.People'>
        # print(class_name)  # People
        # print(class_bases)  # ()
        # print(class_dic)  # {'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x7fe8f96bfe60>, 'say': <function People.say at 0x7fe8f96bfef0>}

        if not class_name.istitle():
            raise NameError('类名的首字母必须大写')

    def __new__(cls, *args, **kwargs):
        # 造Mymeta的对象
        # return type.__new__(cls, *args, **kwargs)
        return super().__new__(cls, *args, **kwargs)

 

自定义元类控制类的调用

class Foo:
    def __call__(self, *args, **kwargs):
        print(self)
        print(args)
        print(kwargs)

obj=Foo()
#1、要想让obj这个对象变成一个可调用的对象,需要在该对象的类中定义一个方法__call__方法,该方法会在调用对象时自动触发
#2、调用obj的返回值就是__call__方法的返回值
res=obj(1,2,3,x=1,y=2)

调用一个对象,就是触发对象所在类中的__call__方法的执行,如果把类也当做一个对象,那么类这个对象的类中也必然存在一个__call__方法

# 如果想让一个对象可以加括号调用, 需要在该对象的类中添加一个方法__call__
class Mymeta(type):

    def __call__(self, *args, **kwargs):
        # 1. Mymeta.__call__函数内会先调用People内的__new__
        people_obj = self.__new__(self)
        # 2. Mymeta.__call__函数会调用People内的__init__
        self.__init__(people_obj, *args, **kwargs)
        # 3. Mymeta.__call__函数内会返回一个初始化好的对象
        return people_obj


class People(metaclass=Mymeta):

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say(self):
        print('%s:%s' % (self.name, self.age))

    def __new__(cls, *args, **kwargs):
        # 产生真正的对象
        return object.__new__(cls)
默认的, 产生类的时候会做三件事情:
# People = Mymeta(People, (object, ), {...}) -> type.__call__
# 1. type.__call__函数内会先调用Mymeta内的__new__
# 2. type.__call__函数会调用Mymeta内的__init__
# 3. type.__call__函数内会返回一个初始化好的对象

默认的, 调用类的时候会做三件事情
# obj = People('featherwit', 18) -> Mymeta.__call__()
# 1. Mymeta.__call__函数内会先调用People内的__new__
# 2. Mymeta.__call__函数会调用People内的__init__
# 3. Mymeta.__call__函数内会返回一个初始化好的对象

 

元类的使用案例

实现单例模式

# 1. __new__()方法实现单例
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,"_instance"):
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance


s1 = Singleton()
s2 = Singleton()

print(s1 is s2)

# 2. 元类实现单例
class Singleton(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super(Singleton,self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton,self).__call__(*args, **kwargs)
        return self.__instance


class Foo(metaclass=Singleton):
    pass

 

推荐阅读