python 元类

元类在Python中用于创建类,所有类都是由type或其子类实例化得到的。元类可以通过`type`函数直接使用,或者通过继承`type`创建。文章介绍了如何使用元类创建带有属性和方法的类,以及如何指定类的元类。元类的主要用途在于对类的定制,例如限制类的命名规则或实现单例模式。示例中展示了如何通过元类实现一个确保类名以特定前缀开始的验证,以及一个简单的单例模式实现。
摘要由CSDN通过智能技术生成

元类type介绍

我们知道在python中一切皆对象,即使是类,也是对象,那么类这个对象是的类是谁呢?那就是元类

通过 type()obj.__class__ 可查看对象的元类:

# 元类
class A:
    pass


if __name__ == "__main__":
    a = A()
    # 1.查看自定义对象的元类
    print(type(a))  # <class '__main__.A'>
    # 2.查看类A的元类
    print(type(A))  # <class 'type'>
    # 3.python一些内置类型的元类
    print(type(str))  # <class 'type'>
    print(type(int))  # <class 'type'>
    print(type(float))  # <class 'type'>
    # 4.甚至type类的元类还是自己
    print(type(type))  # <class 'type'>

从上面结果可以看出:

  1. 普通对象的元类可直接查看是创建对象的类
  2. 自己创建的一些类的元类是type
  3. python内置的一些基本类型的元类是type
  4. type自己的元类也是type

所以可以说:所有的类终归都是由 type 实例化得来的,它就是最元始的,同时它自己也是一个类,所以也可以称之为元类(Metaclass)。

实例、类、元类之间的关系如图所示:

在这里插入图片描述

使用元类创建类

既然前面说了所有的类终究都是由元类创建的,那么下面看看,除了我们经常使用class关键字来快速创建类,如何使用元类来创建类。

通常有以下两种方法

直接使用type

type源码如下

class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """
    def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
        """
        type(object_or_name, bases, dict)
        type(object) -> the object's type
        type(name, bases, dict) -> a new type
        # (copied from class doc)
        """
        pass

创建type对象一般有3个参数:

  • name:新类的名称;
  • bases:以元组的形式,声明父类;
  • dict:以字典的形式声明类的属性;

示例如下:

# 元类

if __name__ == "__main__":
    # 通过type创建类A
    A = type("A", (), {})
    # 创建类A的实例a
    a = A()

    print(type(a))  # <class '__main__.A'>
    print(type(A))  # <class 'type'>
    print(A.__bases__)  # (<class 'object'>,)

从结果可以看出我们使用type成功创建一个类A。

虽然没有给 bases 赋值,但是 Python 中所有的类都是以 object 为基类,所以查看类 Foo1 的父类,会得到这样一个结果。PS:__bases__查看父类。

通过type创建带属性的类:
注意属性是类属性

if __name__ == "__main__":
    User = type("User", (), {"name":"user"})
    my_obj = User()
    print(my_obj)
    # my_obj实例有属性
    print(my_obj.name)

通过type创建带方法的类

def say(self):
    return "i am user"
    # return self.name

if __name__ == "__main__":
    User = type("User", (), {"name":"user", "say":say})
    my_obj = User()
    print(my_obj)
    print(my_obj.say())

通过type创建带父类的类

def say(self):
    return "i am user"
    # return self.name

class BaseClass:
    def answer(self):
        return "i am baseclass"

if __name__ == "__main__":
    User = type("User", (BaseClass, ), {"name":"user", "say":say})
    my_obj = User()
    print(my_obj)
    print(my_obj.answer())

继承type

如果一个类继承了type类,那么这个类也是元类,使用metaclass可以指定某个类的元类是哪个。来这种方式使用的较多。

下面是一个简单的示例:

class MetaClass(type):
    pass


class User(metaclass=MetaClass):
    pass


if __name__ == "__main__":
    my_obj = User()
    print(my_obj)

MetaClass 继承了 type ,就可以称为元类,而 User 类与之前定义的类的不同之处就在于,类名后括号内添加了参数 metaclass 来指定元类(即指定使用哪个元类来创建当前类,默认是type),这样就利用了元类 MetaClass 创建了类 User

类和对象的创建过程

上面知道了如何去定义一个元类,下面看看,元类是如何创建一个类的。

看一个下面的例子:

class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        print("new of MetaClass")
        return super(MetaClass, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print("init of MetaClass")
        super(MetaClass, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("call of MetaClass")
        super(MetaClass, self).__call__( *args, **kwargs)

class User(metaclass=MetaClass):
    pass

直接运行文件:打印如下:

new of MetaClass
init of MetaClass

可以看到与对象的实例化过程很像。

当使用class关键字定义一个类时,其实是先去寻找这个类的元类,找到元类后,先调用元类的__new__方法实例化一个类对象,然后调用__init__去初始化这个类对象。

另外:
创建类时,会先检查当前类是否有元类,没有再找父类是否有,没有再去模块中找,再没有再使用type创建类。

然后我使用User类来创建一个对象:

class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        print("new of MetaClass")
        return super(MetaClass, cls).__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print("init of MetaClass")
        super(MetaClass, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("call of MetaClass")
        super(MetaClass, self).__call__(self, *args, **kwargs)


class User(metaclass=MetaClass):
    pass


if __name__ == "__main__":
    print("--- 开始创建User对象")
    my_obj = User()

运行打印结果如下:

new of MetaClass
init of MetaClass
--- 开始创建User对象
call of MetaClass

在使用元类的__new__方法和__init__去创建完类后,实例化这个类时,会调用元类的__call__方法,这个方法会返回实例化后的对象。

其实在实例化对象时,这个元类的__call__方法会自己调类的__new__方法和__init__去创建并初始化对象,然后返回。

看下面的示例:

class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        print("new of MetaClass")
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        print("init of MetaClass")
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print("call of MetaClass")
        return super().__call__(*args, **kwargs)


class User(metaclass=MetaClass):
    def __new__(cls, *args, **kwargs):
        print("new in User")
        return super().__new__(cls)

    def __init__(self, name, age):
        print("init in User")
        self.name = name
        self.age = age



if __name__ == "__main__":
    print("--- 开始创建User对象")
    my_obj = User("a", 10)
    print(my_obj)

打印结果如下:

new of MetaClass
init of MetaClass
--- 开始创建User对象
call of MetaClass
new in User
init in User
<__main__.User object at 0x7f9b58056d30>

可以很清楚的看到从创建类到类的实例化的过程,可总结如下:

  1. 创建类:调用原元类的__new__方法和__init__方法创建了一个类;
  2. 创建对象:调用了原类的__call__方法;
  3. 创建对象:__call__方法调类的__new__方法和__init__去创建并初始化对象,然后返回。

元类有什么用

一般我们不会自己定义元类,但是会再一些框架只能怪经常见到。

其主要作用就是在定义一些类时有一些限制或特殊要求。

比如我想限制一个测试类必须以Test开头:

class TestType(type):
    def __new__(cls, name, bases, attrs):
        if not name.startswith("Test"):
            raise AttributeError("类名请以Test开头")
        return super().__new__(cls,  name, bases, attrs)


class TestUser(metaclass=TestType):
    pass


if __name__ == "__main__":
    print("--- 开始创建对象")
    test_obj = TestUser()

如果我定义了类不以Test开头就会报错

class TestType(type):
    def __new__(cls, name, bases, attrs):
        if not name.startswith("Test"):
            raise AttributeError("类名请以Test开头")
        return super().__new__(cls,  name, bases, attrs)


class User(metaclass=TestType):
    pass


if __name__ == "__main__":
    print("--- 开始创建对象")
    test_obj = User()
	# AttributeError: 类名请以Test开头

使用元类实现一个单例模式

所谓的单例模式,就是指一个类不论我实例化多少次,都指返回同一个对象。主要用在一些公用资源、文件对象等。比如我们的数据库连接池。

示例如下:

class Meta(type):
    __instance = None

    def __call__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = type.__call__(cls, *args, **kwargs)
        return cls.__instance


class DbPool(metaclass=Meta):
    pass


if __name__ == "__main__":
    x = DbPool()
    y = DbPool()
    print(x)
    print(y)
    print(x==y)
    
    # <__main__.DbPool object at 0x7fc210182e20>
    # <__main__.DbPool object at 0x7fc210182e20>
    # True

上面主要是让我们的DbPool类使用我们自定义的元类Meta。然后重写了Meta例的__call__方法。为什么呢?回一下前面讲的,在实例化对象时,会调用元类的__call__方法返回实例化后的对象,所以为了返回同一个对象,可以在__call__方法里返回同一个即可。

参考:
https://www.cnblogs.com/XiaoYang-sir/p/16524775.html
https://blog.csdn.net/weixin_43988680/article/details/123903473

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值