python 元类

这篇具有很好参考价值的文章主要介绍了python 元类。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

元类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)。

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

python 元类

使用元类创建类

既然前面说了所有的类终究都是由元类创建的,那么下面看看,除了我们经常使用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文章来源地址https://www.toymoban.com/news/detail-447127.html

到了这里,关于python 元类的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python通过函数和常规类编写元类

    python可以通过函数和常规类编写元类。函数和常规类之间通过type()创建类对象。 python工厂函数定义了一个外部函数,它生成并返回一个嵌套函数,所以调用工厂函数就可以得到嵌套函数的引用。参考《python嵌套函数作用域》 任何可调用对象都可以作为一个元类,比如函数,

    2024年01月20日
    浏览(38)
  • ET介绍—— 一切皆实体的设计

    目前十分流行ECS设计,主要是守望先锋的成功,引爆了这种技术。守望先锋采用了状态帧这种网络技术,客户端会进行预测,预测不准需要进行回滚,由于组件式的设计,回滚可以只回滚某些组件即可。ECS最重要的设计是逻辑跟数据的完全分离。即EC是纯数据,System实际上就

    2024年02月05日
    浏览(31)
  • Python基础之面向对象:8、面向对象之元类

    目录 面向对象之元类 一、什么是元类 二、元类推导流程 三、创建类的方式 方式一: 方式二: 四、元类定制类的产生行为 五、元类定制对象的产生行为 六、元类之双下new Python中一切皆为对象,对象是有类实例化生成; 类也是对象(类对象),生成类对象的类可称之为元

    2024年01月20日
    浏览(56)
  • 系统学习Python——装饰器:类装饰器-[初探类装饰器和元类]

    分类目录:《系统学习Python》总目录 函数装饰器是如此有用,以至于Python2.X和Python3.X都扩展了这一模式,允许装饰器应用于类和函数。简而言之,类装饰器类似于函数装饰器,但它们是在一条 class 语句的末尾运行,并把一个类名重新绑定到一个可调用对象。同样,它们可以

    2024年02月19日
    浏览(38)
  • Python黑魔法揭秘:装饰器、生成器、异步编程、GIL、描述符和元类

    Python中的某些特性被看作是“黑魔法”,原因在于它们的强大功能和复杂性。接下来,让我们深入探索这些特性。 装饰器是修改函数或类行为的强大工具,它提供了一种可读性强、代码重用的方式来增强或修改函数或类的行为。装饰器就像一个包裹原函数或类的外壳,能够在

    2024年02月14日
    浏览(46)
  • Python的十二道编程题,码住战胜一切

    一、计算文件大小 二、三级菜单 三、文件监听 四、发红包 五、递归问路 找到路后会把结果返回给上一个调用者’linhaifeng’ ‘linhaifeng’会把结果返回给上一个调用者’yuanhao’… 最终会把结果返回给最开始的调用者inner 六、人狗大战 定义一个人的类 定义一个狗的类 定义一

    2024年02月14日
    浏览(42)
  • python安装三方库教程:关于pip命令的一切,到底怎么用?

      看这篇文章的目录,大家会发现写的很详细,适合收藏哦。如果你是刚学python的小白也没关系!看完这篇文章,关于pip的一切你就懂了。   关于pip的命令需要使用命令行,那么打开命令行界面: win+s/win+r快捷键都行,然后输入cmd后回车就能调出命令行界面了   python以

    2023年04月27日
    浏览(38)
  • Elasticsearch:关于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x

    在本文中,我们将讨论如何在 Python 中使用 Elasticsearch。 如果你还不了解 Elasticsearch,可以阅读这篇文章 “Elasticsearch 简介” 进行快速介绍。在我之前的文章 “Elasticsearch:使用最新的 Python client 8.0 来创建索引并搜索”,我也有所介绍如何使用 Python 客户端来连接 Elasticsearch

    2024年02月02日
    浏览(30)
  • 元类(一)

    元类在工作中一般很少用到,除非手写框架 1、__new__创建类时调用 2、__call__创建与初始化类实例时调用  

    2024年02月11日
    浏览(39)
  • 友元函数与友元类

    实验介绍 私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。声明为友元函数或友元类后就可以直接访问类中所有成员,但同时也破坏了类的封装性。 为什么在模板篇中讲解友元函数和友元类呢? 答

    2024年01月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包