纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Python黑魔法metaclass Python黑魔法之metaclass详情

somenzz Python七号   2021-09-15 我要评论
想了解Python黑魔法之metaclass详情的相关内容吗somenzz Python七号在本文为您仔细讲解Python黑魔法metaclass的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Python的黑魔法metaclass,Python的黑魔法,黑魔法metaclass下面大家一起来学习吧

关于Python 黑魔法 metaclass 的两种极端观点:

  • 这种特性太牛逼了是无所不能的阿拉丁神灯必须找机会用上才能显示自己的 Python 实力
  • 这个特性太危险会蛊惑人心去滥用一旦打开就会释放恶魔让代码难以维护

今天我们就来看看metaclass 到底是阿拉丁神灯还是潘多拉魔盒

一、什么是 metaclass

很多书都会翻译成 元类仅从字面理解 meta 的确是元本源翻译没毛病但理解时应该把元理解为描述数据的超越数据事实上metaclass 的 meta 起源于希腊词汇 meta包含两种意思:

  • Beyond”例如技术词汇 metadata意思是描述数据的超越数据
  • Change”例如技术词汇 metamorphosis意思是改变的形态

因此可以理解为 metaclass 为描述类的超类同时可以改变子类的形态你可能会问了这和元数据的定义差不多么这种特性在编程中有什么用?

用处非常大在没有 metaclass 的情况下子类继承父类父类是无法对子类执行操作的但有了 metaclass就可以对子类进行操作就像装饰器那样可以动态定制和修改被装饰的类metaclass 可以动态的定制或修改继承它的子类

二、metaclass 能解决什么问题?

你已经知道了 metaclass 可以像装饰器那样定制和修改继承它的子类这里就说下它能解决什么实际问题比方说在一个智能语音助手的大型项目中我们有 1 万个语音对话场景每一个场景都是不同团队开发的作为智能语音助手的核心团队成员你不可能去了解每个子场景的实现细节

在动态配置实验不同场景时经常是今天要实验场景 A 和 B 的配置明天实验 B 和 C 的配置光配置文件就有几万行量级工作量不可谓不小而应用这样的动态配置理念我就可以让引擎根据我的文本配置文件动态加载所需要的 Python 类

如果你还不是很清楚那么 YAML 你应该知道它是一个家喻户晓的 Python 工具可以方便地序列化和反序列化数据YAMLObject 可以让它的任意子类支持序列化和反序列化(serialization & deserialization)

序列化和反序列化:

  • 序列化:当程序运行时所有的变量或者对象都是存储到内存中的一旦程序调用完成这些变量或者对象所占有的内存都会被回收而为了实现变量和对象持久化的存储到磁盘中或在网络上进行传输我们需要将变量或者对象转化为二进制流的方式而将其转化为二进制流的过程就是序列化
  • 反序列化:而反序列化就是说程序运行的时候不能从磁盘中进行读取需要将序列化的对象或者变量从磁盘中转移到内存中同时也会将二进制流转换为原来的数据格式我们把这一过程叫做反序列化

现在你有 1 万个不同格式的 YAML 配置文件本来你需要写 1 万个类来加载这些配置文件有了 metaclass你只需要实现一个 metaclass 超类然后再实现一个子类继承这个 metaclass就可以根据不同的配置文件自动拉取不同的类这极大地提高了效率

三、通过一个实例来理解 metaclass

请手动在 ipython 中搞代码看看每一步都输出了什么这样可以彻底的理解类的创建和实例化步骤

In[15]: class Mymeta(type):
   ...:     def __init__(self, name, bases, dic):
   ...:         super().__init__(name, bases, dic)
   ...:         print('===>Mymeta.__init__')
   ...:         print(self.__name__)
   ...:         print(dic)
   ...:         print(self.yaml_tag)
   ...: 
   ...:     def __new__(cls, *args, **kwargs):
   ...:         print('===>Mymeta.__new__')
   ...:         print(cls.__name__)
   ...:         return type.__new__(cls, *args, **kwargs)
   ...: 
   ...:     def __call__(cls, *args, **kwargs):
   ...:         print('===>Mymeta.__call__')
   ...:         obj = cls.__new__(cls)
   ...:         cls.__init__(cls, *args, **kwargs)
   ...:         return obj
   ...: 
In[16]: 
In[16]: 
In[16]: class Foo(metaclass=Mymeta):
   ...:     yaml_tag = '!Foo'
   ...: 
   ...:     def __init__(self, name):
   ...:         print('Foo.__init__')
   ...:         self.name = name
   ...: 
   ...:     def __new__(cls, *args, **kwargs):
   ...:         print('Foo.__new__')
   ...:         return object.__new__(cls)
   ...:     
===>Mymeta.__new__
Mymeta
===>Mymeta.__init__
Foo
{'__module__': '__main__', '__qualname__': 'Foo', 'yaml_tag': '!Foo', '__init__': <function Foo.__init__ at 0x0000000007EF3828>, '__new__': <function Foo.__new__ at 0x0000000007EF3558>}
!Foo

In[17]: foo = Foo('foo')
===>Mymeta.__call__
Foo.__new__
Foo.__init__

In[18]:

从上面的运行结果可以发现在定义 class Foo() 定义时会依次调用 MyMeta __new__ __init__ 方法构建 Foo 类然后在调用 foo = Foo() 创建类的实例对象时才会调用 MyMeta 的 __call__ 方法来调用 Foo 类的 __new__ __init__ 方法

把上面的例子运行完之后就会明白很多了正常情况下我们在父类中是不能对子类的属性进行操作但是元类可以换种方式理解元类、装饰器、类装饰器都可以归为元编程

四、Python 底层语言设计层面是如何实现 metaclass 的?

要理解 metaclass 的底层原理你需要深入理解 Python 类型模型下面将分三点来说明

1、所有的 Python 的用户定义类都是 type 这个类的实例

可能会让你惊讶事实上类本身不过是一个名为 type 类的实例在 Python 的类型世界里type 这个类就是造物的上帝这可以在代码中验证:

In [2]: # Python 3和Python 2类似
   ...: class MyClass:
   ...:   pass
   ...:
   ...: instance = MyClass()
   ...:
in [3]: type(instance)
   ...:
Out[2]: __main__.MyClass
In [4]: type(MyClass)
   ...:
Out[4]: type
In [5]:


你可以看到instance MyClass 的实例而 MyClass 不过是“上帝” type 的实例

2、用户自定义类只不过是 type 类的 __call__ 运算符重载

当我们定义一个类的语句结束时真正发生的情况是 Python 调用 type 的 __call__ 运算符简单来说当你定义一个类时写成下面这样时:

class MyClass:
    data = 1


Python 真正执行的是下面这段代码:

class = type(classname, superclasses, attributedict)


这里等号右边的 type(classname, superclasses, attributedict)就是 type 的 __call__ 运算符重载它会进一步调用:

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

当然这一切都可以通过代码验证比如

In [5]: class MyClass:
   ...:     data = 1
   ...:
   ...: instance = MyClass()
   ...:

In [6]: MyClass, instance
   ...:
Out[6]: (__main__.MyClass, <__main__.MyClass at 0x4ef5188>)

In [7]: instance.data
   ...:
Out[7]: 1

In [8]: MyClass = type('MyClass', (), {'data': 1})
   ...: instance = MyClass()
   ...:

In [9]: MyClass, instance
   ...:
Out[9]: (__main__.MyClass, <__main__.MyClass at 0x4f40748>)

In [10]: instance.data
    ...:
Out[10]: 1

In [11]:

由此可见正常的 MyClass 定义和你手工去调用 type 运算符的结果是完全一样的

3、“超越变形”正常的类

metaclass 是 type 的子类通过替换 type 的 __call__ 运算符重载机制“超越变形”正常的类

其实理解了以上几点我们就会明白正是 Python 的类创建机制给了 metaclass 大展身手的机会

一旦你把一个类型 MyClass metaclass 设置成 MyMetaMyClass 就不再由原生的 type 创建而是会调用 MyMeta __call__ 运算符重载

class = type(classname, superclasses, attributedict) 
# 变为了
class = MyMeta(classname, superclasses, attributedict)

四、使用 metaclass 的风险

不过凡事有利必有弊尤其是 metaclass 这样“逆天”的存在正如你所看到的那样metaclass 会"扭曲变形"正常的 Python 类型模型所以如果使用不慎对于整个代码库造成的风险是不可估量的

换句话说metaclass 仅仅是给小部分 Python 开发者在开发框架层面的 Python 库时使用的而在应用层metaclass 往往不是很好的选择

总结:

本文从 Python 类创建的过程帮助你理解 metaclass 的作用

metaclass 是黑魔法使用得当就是天堂反之就是地狱


相关文章

猜您喜欢

网友评论

Copyright 2020 www.sopisoft.net 【绿软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式