翼度科技»论坛 编程开发 python 查看内容

python的metaclass

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
python中的metaclass可谓熟悉而又陌生,自己开发时很少用,阅读源码时却经常遇到,那么到底什么是metaclass呢?何时使用metaclass呢?
动态创建class的方法

假设我们需要动态创建一个class,那么一般我们有这样几种方法

  • 通过一个函数动态创建class
  • 通过type动态创建class
1.函数动态创建class
  1. def create_class_by_name(name):
  2.     if name == 'dog':
  3.         class Dog(object):
  4.             pass
  5.         return Dog
  6.     else:
  7.         class Cat(object):
  8.             pass
  9.         return Cat
  10. dy_class = create_class_by_name('hi')
  11. print dy_class    # output: <class '__main__.Cat'>
  12. print dy_class()  # output: <__main__.Cat object at 0x03601D10>
复制代码
2.type动态创建class

type 除了可以获取到一个对象的类型,还有另外一个功能:动态创建 class。
它的函数签名是这样的:_type(name, bases, dict) -> a new type_
其中:

  • name: 类名
  • bases: 父类名的tuple,用于继承,可以为空
  • dict: 字典,包含class attributes的 name 和 value
  1. class ClassParent(object):
  2.     first_name = 'John'
  3. # 创建一个 继承自 ClassParent 的 ClassChild,并为 ClassChild 添加一个 age = 15 的 attribute
  4. child_class = type('ClassChild', (ClassParent,), {'age': 15})
  5. # child_class 形如:
  6. class ClassChild(ClassParent):
  7.     age = 15
  8. child_obj = child_class()
  9. print child_obj.first_name, child_obj.age
  10. # output: John 15
复制代码
事实上,type 关键字,是 python 用来创建 class 的 metaclass。可以通过 __class__ 来查看一个 class 的 metaclass:
  1. print child_class.__class__
  2. # output <type 'type'>
复制代码
使用metaclass创建class

metaclass,即是(class of class) class 的 class,用来描述如何创建一个 class 的代码段。
python2

在 class 的定义中,可以通过 __metaclass__ 来指定当前 class 的 metaclass:
因此,只要我们指定了__metaclass__就可以代替type()创建class.我们自己来写一个最简单的metaclass.
  1. class DemoMeta(type):
  2.         pass
  3. class DemoClass(object):
  4.         __metaclass__ = DemoMeta
  5. print type(DemoClass) #<class '__main__.DemoMeta'>
复制代码
看一个复杂些的例子
  1. class FooMeta(type):
  2.         def __new__(mcs, name, bases, attrs):
  3.                 """
  4.                 定制创建 class 的行为
  5.                 作为示例,这里将外部传入的 attrs 的名称做一些处理:如果以'_'开头,则转为小写
  6.                 :param name: class 名称
  7.                 :param bases: tuple, 父类的名称
  8.                 :param attrs: class attributes
  9.                 """
  10.                 converted = {atr if not atr.startswith('_') else atr.lower(): v
  11.                      for atr, v in attrs.items()}
  12.                 cls = super(FooMeta, mcs).__new__(mcs, name, bases, converted)
  13.                 return cls
  14. class Foo(object):
  15.     __metaclass__ = FooMeta
复制代码
python3

py3中,指定元类的语法有一点小小的修改:不再使用 __metaclass__,而是在定义 class 时显式地指定 metaclass:
  1. class Foo(object, metaclass=CustomMetaclass):
  2.     pass
复制代码
常见用途

metaclass可以控制类的创建过程,包括类的属性、方法和父类等。metaclass可以用于实现一些高级的编程技巧,例如自动注册子类、自动添加属性和方法等

  • 统计某种类型
  • 定义一个单例
  • 自动添加属性和方法
如何统计某个类的所有子类

猜想一下,统计某个类的所有子类
__bases__是一个元组,包含了一个类的所有直接父类,所以不不能统计到某种类型
还有一种方法:
使用gc.get_objects()函数获取所有已经创建的对象,然后使用issubclass()函数判断一个类是否是另一个类的子类,从而统计所有的子类
以下是一个示例代码:
  1. import gc
  2. def count_subclasses(cls):
  3.     count = 0
  4.     for obj in gc.get_objects():
  5.         if isinstance(obj, type) and issubclass(obj, cls):
  6.             count += 1
  7.     return count
复制代码
自动统计某种类型

下面是一个简单的例子演示了如何使用metaclass来自动注册子类。
假设我们有一个基类Base,我们希望所有继承自Base的子类都能够自动注册到一个全局的字典中。我们可以定义一个Meta类,该类继承自type,并重写其__init__方法,在该方法中实现自动注册的逻辑。然后,我们将Base类的metaclass设置为Meta类,这样所有继承自Base的子类都会使用Meta类来创建实例,并自动注册到全局字典中。
  1. class Meta(type):
  2.     registry = {}
  3.     def __init__(cls, name, bases, attrs):
  4.         super(Meta, cls).__init__(name, bases, attrs)
  5.         if name != 'Base':
  6.             Meta.registry[name] = cls
  7. class Base(object):
  8.     __metaclass__ = Meta
  9. class Subclass1(Base):
  10.     pass
  11. class Subclass2(Base):
  12.     pass
  13. print Meta.registry
复制代码
输出结果为:
  1. {'Subclass1': <class '__main__.Subclass1'>, 'Subclass2': <class '__main__.Subclass2'>}
复制代码
可以看到,Subclass1和Subclass2都被自动注册到了Meta.registry字典中。这样,我们就可以方便地获取所有继承自Base的子类了。
定义单例
  1. class Singleton(type):
  2.     def __init__(cls, name, bases, dict):
  3.         super(Singleton, cls).__init__(name, bases, dict)
  4.         cls.instance = None
  5.     def __call__(cls, *args):
  6.         if cls.instance is None:
  7.             cls.instance = super(Singleton, cls).__call__(*args)
  8.         return cls.instance
  9. class MyCard(object):
  10.         __metaclass__ = Singleton
  11. def testSingle():
  12.         card1 = MyCard()
  13.         card2= MyCard()
  14.         print card1,card2
  15.     #输出结果:<__main__.MyCard object at 0x03A6FE90> <__main__.MyCard object at 0x03A6FE90>
复制代码
自动添加属性和方法

假设我们有一个基类Base,我们希望所有继承自Base的子类都能够自动添加一个名为name的属性和一个名为hello的方法。我们可以定义一个Meta类,该类继承自type,并重写其__init__方法,在该方法中实现自动添加属性和方法的逻辑。然后,我们将Base类的metaclass设置为Meta类,这样所有继承自Base的子类都会使用Meta类来创建实例,并自动添加name属性和hello方法。
  1. class Meta(type):
  2.     def __init__(cls, name, bases, attrs):
  3.         super(Meta, cls).__init__(name, bases, attrs)
  4.         cls.name = name
  5.         cls.hello = lambda self: 'Hello, %s!' % self.name
  6. class Base(object):
  7.     __metaclass__ = Meta
  8. class Subclass1(Base):
  9.     pass
  10. class Subclass2(Base):
  11.     pass
  12. print Subclass1().hello()
  13. print Subclass2().hello()
  14. #Hello, Subclass1!
  15. #Hello, Subclass2!
复制代码
Python选取 metaclass 的策略

在Python中,当我们定义一个类时,解释器会根据以下顺序来选择metaclass:

  • 如果该类显式指定了metaclass,则使用该metaclass。
  • 否则,如果该类的父类中有metaclass,则使用该metaclass。
  • 否则,如果该类的模块中有metaclass,则使用该metaclass。
  • 否则,如果该类的基类中有metaclass,则使用该metaclass。
  • 否则,使用默认的type作为metaclass。
结尾

如果看完之后你还是看不懂,没关系,99%的情况下都不需要用到metaclass

来源:https://www.cnblogs.com/zhaoqingqing/p/17951351
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具