metaclass 的超越变形特性有什么用?

  来看yaml的实例:
import yaml
class Monster(yaml.YAMLObject):
yaml_tag = u'!Monster'
def __init__(self, name, hp, ac, attacks):
self.name = name
self.hp = hp
self.ac = ac
self.attacks = attacks
def __repr__(self):
return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
self.__class__.__name__, self.name, self.hp, self.ac,
self.attacks) monster1 = yaml.load("""
--- !Monster
name: Cave spider
hp: [2,6] # 2d6
ac: 16
attacks: [BITE, HURT]
""",Loader=yaml.Loader) print(monster1)
#Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
print(type(monster1)) #<class '__main__.Monster'> print (yaml.dump(Monster(
name='Cave lizard', hp=[3,6], ac=16, attacks=['BITE','HURT']))
) # dump() 返回 str
# 输出
# !Monster
# ac: 16
# attacks: [BITE, HURT]
# hp: [3, 6]
# name: Cave lizard

  上面的代码调用yaml.load(),就能把任意一个 yaml 序列载入成一个 Python Object;而调用yaml.dump(),就能把一个 YAMLObject 子类序列化。对于 load() 和 dump() 的使用者来说,他们完全不需要提前知道任何类型信息,这让超动态配置编程成了可能。

  只要简单地继承 yaml.YAMLObject,就能让你的 Python Object 具有序列化和逆序列化能力。
 

metaclass 的超越变形特性怎么用?

  YAML 怎样用 metaclass 实现动态序列化 / 逆序列化功能,看其源码

#Python 2/3 相同部分
class YAMLObjectMetaclass(type):
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
# 省略其余定义 # Python 3
class YAMLObject(metaclass=YAMLObjectMetaclass):
yaml_loader = Loader
# 省略其余定义 # Python 2
class YAMLObject(object):
__metaclass__ = YAMLObjectMetaclass
yaml_loader = Loader
# 省略其余定义

  YAMLObject 把 metaclass 都声明成了 YAMLObjectMetaclass

  在你定义任何 YAMLObject 子类时,Python 会强行插入运行下面这段代码
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)

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

  第一,所有的 Python 的用户定义类,都是 type 这个类的实例。

class MyClass:
pass instance = MyClass() print(type(instance))
# 输出
#<class '__main__.MyClass'> print(type(MyClass))
# 输出
#<class 'type'>

  instance 是 MyClass 的实例,而 MyClass 不过是“上帝”type 的实例。

  
  第二,用户自定义类,只不过是 type 类的__call__运算符重载。
 
class MyClass:
data = 1 instance = MyClass()
print(MyClass, instance)
# 输出
#(__main__.MyClass, <__main__.MyClass instance at 0x7fe4f0b00ab8>)
print(instance.data)
# 输出
# MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
print(MyClass, instance)
# 输出
#(__main__.MyClass, <__main__.MyClass at 0x7fe4f0aea5d0>) print(instance.data)
# 输出
#

  可以看出,定义Myclass的时候Python实际调用的是type(classname, superclasses, attributedict),就是 type 的__call__运算符重载,接着会进一步调用

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

    

  第三,metaclass 是 type 的子类,通过替换 type 的__call__运算符重载机制,“超越变形”正常的类。
  一旦你把一个类型 MyClass 的 metaclass 设置成 MyMeta,MyClass 就不再由原生的 type 创建,而是会调用 MyMeta 的__call__运算符重载。
class = type(classname, superclasses, attributedict)
# 变为了
class = MyMeta(classname, superclasses, attributedict)

  

使用 metaclass 的风险

  正如你所看到的那样,metaclass 会"扭曲变形"正常的 Python 类型模型。所以,如果使用不慎,对于整个代码库造成的风险是不可估量的。换句话说,metaclass 仅仅是给小部分 Python 开发者,在开发框架层面的 Python 库时使用的。而在应用层,metaclass 往往不是很好的选择。

  

参考

  极客时间《Python 核心技术与实战》

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)
obj.testPerporet = 'change' #修改子类的属性
cls.__init__(cls, *args, **kwargs)
return obj class Foo(metaclass=Mymeta):
yaml_tag = '!Foo'
testPerporet = 'orig' def __init__(self, name):
print('Foo.__init__')
self.name = name def __new__(cls, *args, **kwargs):
print('Foo.__new__')
return object.__new__(cls) foo = Foo('foo')
print(foo.__dict__)

尘墨 提供的参考代码

 

Python进阶:metaclass谈的更多相关文章

  1. python进阶_浅谈面向对象进阶

    python进阶_浅谈面向对象进阶 学了面向对象三大特性继承,多态,封装.今天我们看看面向对象的一些进阶内容,反射和一些类的内置函数. 一.isinstance和issubclass  class F ...

  2. 【python进阶】详解元类及其应用2

    前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...

  3. python进阶篇

    python进阶篇 import 导入模块 sys.path:获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到. ​ import sys ...

  4. Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理)

    Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理) 一丶封装 , 多态 封装:            将一些东西封装到一个地方,你还可以取出来( ...

  5. python进阶强化学习

    最近学习了慕课的python进阶强化训练,将学习的内容记录到这里,同时也增加了很多相关知识. 主要分为以下九个模块: 基本使用 迭代器和生成器 字符串 文件IO操作 自定义类和类的继承 函数装饰器和类 ...

  6. Python魔法 - MetaClass

    Python魔法 - MetaClass metaclass The class of a class. Class definitions create a class name, a class ...

  7. Python进阶:函数式编程实例(附代码)

    Python进阶:函数式编程实例(附代码) 上篇文章"几个小例子告诉你, 一行Python代码能干哪些事 -- 知乎专栏"中用到了一些列表解析.生成器.map.filter.lam ...

  8. Python进阶 - 对象,名字以及绑定

    Python进阶 - 对象,名字以及绑定 1.一切皆对象 Python哲学: Python中一切皆对象 1.1 数据模型-对象,值以及类型 对象是Python对数据的抽象.Python程序中所有的数据 ...

  9. Python进阶-继承中的MRO与super

    Python进阶-继承中的MRO与super 写在前面 如非特别说明,下文均基于Python3 摘要 本文讲述Python继承关系中如何通过super()调用"父类"方法,supe ...

随机推荐

  1. mysql 使用的三个小技巧

    mysql 使用的三个小技巧 快速阅读 Mysql查询工具中如何查询多条语名,Mysql中如何设置变量,Mysql中如何查特定字段,后面再加* Mysql查询工具中如何查询多条语名 默认myslq只能 ...

  2. IdHTTPServer允许跨域访问

    IdHTTPServer允许跨域访问 procedure TMain.idHttpServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTT ...

  3. 今天闲来无事给我这老伙计A4L换个机油

    今天闲来无事给我这老伙计A4L换个机油         今天天气不错正好心血来潮给我的老伙计做个小保健.跟我这么久了也不能亏待是吧,也很久没来论坛了顺便冒个泡给我们版主晶晶交个作业要不然又要揍我了. ...

  4. Python Django使用HttpResponse返回图片并显示

    views.py def read_img(request): """ : 读取图片 :param request: :return: """ ...

  5. centos 安装最新版git

    对个人而言,gitlab有点浪费资源,占内存太大,一个博客服务器,配置比较低,用gitlab太浪费了.(公司使用gitlab,这个适合公司团队使用) 前提条件,放行git端口,防火墙添加放行规则,将3 ...

  6. 123457123456#0#-----com.tym.niuniuChengYu05--前拼后广--最牛成语tym

    com.tym.niuniuChengYu05--前拼后广--最牛成语tym

  7. C#DbHelperOleDb,Access数据库帮助类 (转)

    /// <summary>/// 编 码 人:苏飞/// 联系方式:361983679  /// 更新网站:[url=http://www.sufeinet.com/thread-655- ...

  8. AD 常用策略-修改本地管理员密码,禁用非administrator帐户,删除非administrator帐户

    这个是用启动脚本实现的. 我放在计算机策略下了. 另存为VBS格式即可 修改第三行“123qwe!@#”,引号中的就是你的新密码. 一:禁用非administrator帐户 strComputer = ...

  9. 【计算机视觉】OpenCV篇(9) - 轮廓(寻找/绘制轮廓)

    什么是轮廓? 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 轮廓与边缘好像挺像的? 是的,确实挺像,那么区别是什么呢?简而言之,轮廓是连续的,而边缘并不全都连续(见下图示例).其实边缘主要是 ...

  10. 分类:logistic回归

    import numpy as np import matplotlib.pyplot as plt x=np.linspace(-1,1,200) y=1/(1+np.exp(10*x-1)) pl ...