python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python Metaclass

Python Metaclass原理与实现过程详细讲解

作者:雕弓

MetaClass元类,本质也是一个类,但和普通类的用法不同,它可以对类内部的定义(包括类属性和类方法)进行动态的修改。可以这么说,使用元类的主要目的就是为了实现在创建类时,能够动态地改变类中定义的属性或者方法

前言

学习python时,对 meta class 并不陌生,django 的model, form定义中经常会遇到class Meta 的代码, 但 django中没有介绍模型与表单中的 meta 属性是怎么回事。 python的众多教程,也很少有作者提到,或把meta class 讲清楚,造成大家以为metaclass 很难理解。 今天我尝试用实例代码的方式,让metaclass 更容易理解与使用。

class Person(models.Model):
    cname = models.CharField(max_length=30, verbose_name="姓名")
    user = models.ForeignKey(
        get_user_model(), null=True, on_delete=models.CASCADE)
    birth_date = models.DateField(verbose_name="出生日期")
    def __str__(self):
        return self.cname
    class Meta:
        verbose_name = "人员信息"
        db_table = "tbl_person"

Python与java 一样,宣称自己一切皆对象,且有过之而不及。python的数据类型是类对象,甚至函数本身也是对象。 Python object 是所有类型的基类,各种数据类型, 函数, class 都是object 的派生类,因此,从object的概念上推理,存在某些 class ,其可以生成其它class, 这种 class 就称为meta class., 也称元类.

本文将讨论meta class的定义,以及使用方式。 内容共包含3个主题:

Type 来定义类

1个 class 也是1个object, 其包含若干属性,以及一些方法。 type () 通常用来显示 变量与函数的类型,但也可以用type 来创建 class. 下面我们来演示这一过程。

首先,先用标准的方式,class关键字来创建1个 Food 类。

class Food(object):
    def __init__(self, food):
        self.food = food
    def get_food(self):
        return self.food
def main():
    myfood = Food(food='蛋炒饭')
    print(myfood.get_food())
main()

output

蛋炒饭

下面演示如何用 type来创建1个Food 类

def init(self, food):
    self.food = food
def get_food(self):
    return self.food
Food = type("Food", (object,),  {
    '__init__': init,
    'get_food': get_food,
})
myfood = Food(food="蛋炒饭")
print(myfood.get_food())

output:

蛋炒饭

要创建一个class对象,type()函数依次传入3个参数:

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class

用 type来增加1个子类VegFood, 其父类为Food

def init(self, food):
    self.food = food
def get_food(self):
    return self.food
Food = type("Food", (object,),  {
    '__init__': init,
    'get_food': get_food,
})
def get_vegfood(self):
    return {'清炒菠菜', '干煸豆角'}
# 新建子类,父类为Food
VegFood = type("VegFood", (Food,), {
    "get_vegfood": get_vegfood,
})
veg = VegFood(food="蛋炒饭")
print(veg.get_food())
print(veg.get_vegfood())

output:

蛋炒饭
{'干煸豆角', '清炒菠菜'}

可以看到,子类 VegFood可以继承 父类Food的方法get_food(),也可以使用子类的方法 get_vegfood()

编写 meta class

metaclass,直译为元类,继承自type. 因此,如前一节的示例 ,metaclass 也可以用来创建类。

用metaclass后, 关于类的编程过程: 先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

metaclass的定义过程,主要是实现 __new__ 方法,有4个传入参数, 与 type创建类的参数相似

metaclass还可以实现1个 __call__方法,在该类被调用时执行此方法。

metacalss 示例 :

class MetaCls(type):
    """metaclass的示例"""
    def __new__(cls, clsname, superclasses, attributedict):
        print("clsname:", clsname)
        print("superclasses:", superclasses)
        print("attrdict:", attributedict)
        return super(MetaCls, cls).__new__(cls,
                                           clsname, superclasses, attributedict)
def demo(self):
    print("Info from demo method")
# generate a class, Example 
Example = MetaCls('Example', (object, ), {"demo": demo, })
Example.clsname = "ExampleClass"  # 修改 metaclass的属性值 
print("class type:", type(Example))
myexample = Example()             # 新建1个Example的实例对象
print(myexample.clsname)          # 打印父类属性值
myexample.demo()                  # 打印子类方法

output

clsname: Example
superclasses: (<class 'object'>,)
attrdict: {'demo': <function demo at 0x00000169DF2CC280>}
class type: <class '__main__.MetaCls'>
ExampleClass
Info from demo method

从上例 可以看到, Example类的生成方式,就是 MetaCls类的实例 化过程,example可以添加自己的方法,可以修改从父类继承的属性。

metaclass应用

实际开发中遇到的metaclass是django中定义model, form时,可以定义metaclass的属性,其实就是django的ORM使用了 metaclass的编程方式,由于其实现较复杂,就不深入讨论。

这里我们来看1个更简单,而且非常实用的metaclass的例子: 用metaclass实现单例模式。

单例模式提供了这样一个机制,即确保类有且只有一个特定类型的对象,并提供全局 访问点。因此,单例模式通常用于下列情形,例如日志记录或数据库操作、打印机后台处 理程序,以及其他程序—该程序运行过程中只能生成一个实例,以避免对同一资源产生 相互冲突的请求。

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(
                SingletonMeta, cls).__call__(*args, **kwargs)
        return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
    pass
s = SingletonClass()
print("Object created", s)
s1 = SingletonClass()
print("Object created", s1)

output :

Object created <__main__.SingletonClass object at 0x0000020CF1F20130>
Object created <__main__.SingletonClass object at 0x0000020CF1F20130>

可以看到,SingletonMeta用metaclass的方法定义了1个单例类, SingletonClass继承自SingletonMeta, s 与 s1 虽然分别实例化,但内存中的地址都指向同1个SingletonClass对象,这样就实现了单例化。

而且metaclass实现单例模式,比 object方式实现的单例化有明显的优势,任何类都可以通过继承SingletonMeta实现单例模式, 是不是很酷.

到此这篇关于Python Metaclass原理与实现过程详细讲解的文章就介绍到这了,更多相关Python Metaclass内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文