python的metaclass

本文为作者原创,转载请注明出处:https://www.cnblogs.com/zhaoqingqing/p/17951351



python中的metaclass可谓熟悉而又陌生,自己开发时很少用,阅读源码时却经常遇到,那么到底什么是metaclass呢?何时使用metaclass呢?

动态创建class的方法#

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

  1. 通过一个函数动态创建class
  2. 通过type动态创建class

1.函数动态创建class#

Copy
def create_class_by_name(name): if name == 'dog': class Dog(object): pass return Dog else: class Cat(object): pass return Cat dy_class = create_class_by_name('hi') print dy_class # output: <class '__main__.Cat'> 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
Copy
class ClassParent(object): first_name = 'John' # 创建一个 继承自 ClassParent 的 ClassChild,并为 ClassChild 添加一个 age = 15 的 attribute child_class = type('ClassChild', (ClassParent,), {'age': 15}) # child_class 形如: class ClassChild(ClassParent): age = 15 child_obj = child_class() print child_obj.first_name, child_obj.age # output: John 15

事实上,type 关键字,是 python 用来创建 class 的 metaclass。可以通过 __class__ 来查看一个 class 的 metaclass:

Copy
print child_class.__class__ # output <type 'type'>

使用metaclass创建class#

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

python2#

在 class 的定义中,可以通过 __metaclass__ 来指定当前 class 的 metaclass:

因此,只要我们指定了__metaclass__就可以代替type()创建class.我们自己来写一个最简单的metaclass.

Copy
class DemoMeta(type): pass class DemoClass(object): __metaclass__ = DemoMeta print type(DemoClass) #<class '__main__.DemoMeta'>

看一个复杂些的例子

Copy
class FooMeta(type): def __new__(mcs, name, bases, attrs): """ 定制创建 class 的行为 作为示例,这里将外部传入的 attrs 的名称做一些处理:如果以'_'开头,则转为小写 :param name: class 名称 :param bases: tuple, 父类的名称 :param attrs: class attributes """ converted = {atr if not atr.startswith('_') else atr.lower(): v for atr, v in attrs.items()} cls = super(FooMeta, mcs).__new__(mcs, name, bases, converted) return cls class Foo(object): __metaclass__ = FooMeta

python3#

py3中,指定元类的语法有一点小小的修改:不再使用 __metaclass__,而是在定义 class 时显式地指定 metaclass:

Copy
class Foo(object, metaclass=CustomMetaclass): pass

常见用途#

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

  1. 统计某种类型
  2. 定义一个单例
  3. 自动添加属性和方法

如何统计某个类的所有子类#

猜想一下,统计某个类的所有子类

__bases__是一个元组,包含了一个类的所有直接父类,所以不不能统计到某种类型

还有一种方法:

使用gc.get_objects()函数获取所有已经创建的对象,然后使用issubclass()函数判断一个类是否是另一个类的子类,从而统计所有的子类

以下是一个示例代码:

Copy
import gc def count_subclasses(cls): count = 0 for obj in gc.get_objects(): if isinstance(obj, type) and issubclass(obj, cls): count += 1 return count

自动统计某种类型#

下面是一个简单的例子演示了如何使用metaclass来自动注册子类。

假设我们有一个基类Base,我们希望所有继承自Base的子类都能够自动注册到一个全局的字典中。我们可以定义一个Meta类,该类继承自type,并重写其__init__方法,在该方法中实现自动注册的逻辑。然后,我们将Base类的metaclass设置为Meta类,这样所有继承自Base的子类都会使用Meta类来创建实例,并自动注册到全局字典中。

Copy
class Meta(type): registry = {} def __init__(cls, name, bases, attrs): super(Meta, cls).__init__(name, bases, attrs) if name != 'Base': Meta.registry[name] = cls class Base(object): __metaclass__ = Meta class Subclass1(Base): pass class Subclass2(Base): pass print Meta.registry

输出结果为:

Copy
{'Subclass1': <class '__main__.Subclass1'>, 'Subclass2': <class '__main__.Subclass2'>}

可以看到,Subclass1和Subclass2都被自动注册到了Meta.registry字典中。这样,我们就可以方便地获取所有继承自Base的子类了。

定义单例#

Copy
class Singleton(type): def __init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) cls.instance = None def __call__(cls, *args): if cls.instance is None: cls.instance = super(Singleton, cls).__call__(*args) return cls.instance class MyCard(object): __metaclass__ = Singleton def testSingle(): card1 = MyCard() card2= MyCard() print card1,card2 #输出结果:<__main__.MyCard object at 0x03A6FE90> <__main__.MyCard object at 0x03A6FE90>

自动添加属性和方法#

假设我们有一个基类Base,我们希望所有继承自Base的子类都能够自动添加一个名为name的属性和一个名为hello的方法。我们可以定义一个Meta类,该类继承自type,并重写其__init__方法,在该方法中实现自动添加属性和方法的逻辑。然后,我们将Base类的metaclass设置为Meta类,这样所有继承自Base的子类都会使用Meta类来创建实例,并自动添加name属性和hello方法。

Copy
class Meta(type): def __init__(cls, name, bases, attrs): super(Meta, cls).__init__(name, bases, attrs) cls.name = name cls.hello = lambda self: 'Hello, %s!' % self.name class Base(object): __metaclass__ = Meta class Subclass1(Base): pass class Subclass2(Base): pass print Subclass1().hello() print Subclass2().hello() #Hello, Subclass1! #Hello, Subclass2!

Python选取 metaclass 的策略#

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

  1. 如果该类显式指定了metaclass,则使用该metaclass。
  2. 否则,如果该类的父类中有metaclass,则使用该metaclass。
  3. 否则,如果该类的模块中有metaclass,则使用该metaclass。
  4. 否则,如果该类的基类中有metaclass,则使用该metaclass。
  5. 否则,使用默认的type作为metaclass。

结尾#

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

作者:赵青青   一名在【网易游戏】做游戏开发的程序员,擅长Unity3D,游戏开发,.NET等领域。
本文版权归作者和博客园共有,欢迎转载,转载之后请务必在文章明显位置标出原文链接和作者,谢谢。
如果本文对您有帮助,请点击【推荐】您的赞赏将鼓励我继续创作!想跟我一起进步么?那就【关注】我吧。
posted @   赵青青  阅读(591)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 易语言 —— 开山篇
· Trae初体验
历史上的今天:
2017-01-07 UGUI Text(Label)
点击右上角即可分享
微信分享提示
CONTENTS