python的metaclass使用小结

 更新时间:2024年01月08日 08:58:50   作者:赵青青  
python中的metaclass可谓熟悉而又陌生,自己开发时很少用,阅读源码时却经常遇到,那么到底什么是metaclass呢?何时使用metaclass呢?这篇文章主要介绍了python的metaclass,需要的朋友可以参考下

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

动态创建class的方法

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

  • 通过一个函数动态创建class
  • 通过type动态创建class

1.函数动态创建class

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
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:

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.

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

看一个复杂些的例子

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:

class Foo(object, metaclass=CustomMetaclass):
    pass

常见用途

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

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

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

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

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

还有一种方法:

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

以下是一个示例代码:

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类来创建实例,并自动注册到全局字典中。

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

输出结果为:

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

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

定义单例

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方法。

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:

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

结尾

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

到此这篇关于python的metaclass的文章就介绍到这了,更多相关python的metaclass内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python实现比较扑克牌大小程序代码示例

    Python实现比较扑克牌大小程序代码示例

    这篇文章主要介绍了Python实现比较扑克牌大小程序代码示例,具有一定借鉴价值,需要的朋友可以了解下。
    2017-12-12
  • Python 脚本获取ES 存储容量的实例

    Python 脚本获取ES 存储容量的实例

    今天小编就为大家分享一篇Python 脚本获取ES 存储容量的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-12-12
  • Python面向对象编程之类的概念

    Python面向对象编程之类的概念

    这篇文章主要介绍了Python面向对象编程之类的概念,Python语言的中所有数据类型都是对象、函数是对象、模块是对象;所有类都是继承最基础的类object,面向对象编程又叫OOP,下文了解更多具体内容,需要的朋友可以参考一下
    2021-11-11
  • 用Python实现控制电脑鼠标

    用Python实现控制电脑鼠标

    大家好,本篇文章主要讲的是用Python实现控制电脑鼠标,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • python中文分词,使用结巴分词对python进行分词(实例讲解)

    python中文分词,使用结巴分词对python进行分词(实例讲解)

    下面小编就为大家带来一篇python中文分词,使用结巴分词对python进行分词的实例讲解。有比较好的参考价值,希望能给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • 一步步教你用python给女朋友写个微信自动提醒的程序

    一步步教你用python给女朋友写个微信自动提醒的程序

    如今微信已成为我们日常生活的主要交流工具,但是微信自身的功能有时候可能并不能满足我们的需要,因此我们会想是否可以进行微信功能的拓展呢,这篇文章主要给大家介绍了关于利用python给女朋友写了个微信自动提醒程序的相关资料,需要的朋友可以参考下
    2021-10-10
  • 基于python 字符编码的理解

    基于python 字符编码的理解

    下面小编就为大家带来一篇基于python 字符编码的理解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • Python中关于集合的介绍与常规操作解析

    Python中关于集合的介绍与常规操作解析

    Python除了List、Tuple、Dict等常用数据类型外,还有一种数据类型叫做集合(set),集合的最大特点是:集合里边的元素是不可重复的并且集合内的元素还是无序的
    2021-09-09
  • 浅谈tensorflow1.0 池化层(pooling)和全连接层(dense)

    浅谈tensorflow1.0 池化层(pooling)和全连接层(dense)

    本篇文章主要介绍了浅谈tensorflow1.0 池化层(pooling)和全连接层(dense),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Python读取CSV文件并计算某一列的均值和方差

    Python读取CSV文件并计算某一列的均值和方差

    这篇文章主要介绍了利用Python读取CSV文件并计算某一列的均值和方差,这里利用了csv模块来对文件进行处理,文章通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12

最新评论