当前位置: 移动技术网 > IT编程>脚本编程>Python > Python进阶:metaclass谈

Python进阶:metaclass谈

2019年06月24日  | 移动技术网IT编程  | 我要评论

最终的阿玛格顿,螃蟹的营养价值,兽拳战队国语

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)
# 输出
#1

myclass = type('myclass', (), {'data': 1})
instance = myclass()
print(myclass, instance)
# 输出
#(__main__.myclass, <__main__.myclass at 0x7fe4f0aea5d0>)

print(instance.data)
# 输出
#1

  可以看出,定义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__)

 

 

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网