python 元类

Python进阶:一步步理解Python中的元类metaclass

技术分享

systemime
2021-05-19
6 min

对python 元类对理解

来源知乎: https://zhuanlan.zhihu.com/p/23887627  原作:笑虎

虽然 Python 本身很难说是面向对象语言,但 Python 中的数据模型(data model)可以说真的是 “纯面向对象”。在 Python 的世界里,一切皆是对象。无论是数值、字符串、序列、字典、函数、模块、类、实例、文件等等。

元类(metaclass)是 Python 2.2 以后引入的概念,它的作用是定制类的创建行为。这么解释可能有点难理解,那么这篇文章就通过实例,一步步解释 Python 中的元类。

# 1.Python 中一切皆对象,包括类

class Foo:
    def hello(self):
        print("hello world!")
        return

foo = Foo()
print(type(foo))            # <class '__main__.Foo'>
print(type(foo.hello))      # <class 'method'>
print(type(Foo))            # <class 'type'>

temp = Foo                  # 赋值给其他变量
Foo.var = 11                # 增加参数
print(Foo)                  # 作为函数参数

例子中 type(Foo) 说明 Foo 本身也是一个对象,可以将其赋值给其他对象、对其添加属性、将其作为函数参数传递等等。

# 2. 类的创建过程

在上边的例子中,类 Foo 的创建过程中会执行 class 语句,此时需要首先确定元类(元类定制类的创建行为)。元类的确定过程如下:

  • 确定类 Foo 的父类是否有参数 metaclass,如果没有则:
  • 确定类 Foo 的父类的父类是否有参数 metaclass,如果没有则:
  • 使用默认元类 type(type 的用法会在 3 中讲解)。

上边的例子中,前 2 项都不符合,则直接使用默认元类 type。即上边的语句相当于:

def hello(self):
    print("hello world")
    return

Foo = type("Foo", (object,), {"hello": hello})

此时可以看出,实际上类 Foo 是元类 type 的实例,如下图所示:

元类type

# 3. 动态创建类

Python 中的类可以动态创建,用的就是默认元类 type。动态创建类的 type 函数原型为:

type(object_or_name, bases, dict)

小编注:第一项为类的名字,第二项 bases 为基类,比如 object, list, str 等等,第三项为一个由属性和方法组成的字典。

举个比较复杂的动态创建类的例子:

def init(self, name):
    self.name = name
    return

def hello(self):
    print("hello %s" % self.name)
    return

Foo = type("Foo", (object,), {"__init__": init, "hello": hello, "cls_var": 10})
foo = Foo("xianhu") # 调用__init__方法,初始化给name赋值
print(foo.hello())  # 打印 hello xianhu
print(Foo.cls_var)  # 10

# 4. 自定义元类

再一次说明实例、类和元类之间的关系:

>>> foo.__class__     # <class 'Foo'>
>>> Foo.__class__     # <class 'type'>
>>> type.__class__    # <class 'type'>

foo 是 Foo 的实例,Foo 是 type 的实例,type 的类又是 type。type 是默认的元类。那么如何自定义元类呢?(注意,百分之 99 的工程中不会用到自定义元类,除非你对元类非常理解)

举个例子,假设我们需要对一个模块中的所有函数添加作者属性。首先自定义一个元类,自定义时,需要继承默认元类 type,并重写其中的__new__方法:

class Author(type):
    def __new__(cls, name, bases, dict):
        # 添加作者属性
        dict["author"] = "xianhu"
        return super().__new__(cls, name, bases, dict)

对模块中所有函数的继承类参数中添加 metaclass 参数:

class Foo(object, metaclass=Author):
    pass

foo = Foo()
print(foo.author) # xianhu

注意:Python3 中不再有__metaclass__属性以及模块级别的__metaclass__属性。

-- 完 --

备注:

Python 的 Metaclass(元类) 与 Django 模型中 Meta 选项属于不同事物,目的也不相同。前者主要用于动态定制类的创建行为,而后者模型内的 Meta 类主要作为配置类,存放与各个字段无关的模型配置信息 (英文原文叫 “anything that’s not a field”),比如默认排序方式 (ordering)、数据表明 (db_table)、模型是否为抽象模型 (abstract=True), 模型的单复数名 (verbose_name) 和自定义权限等。

class FooModel(models.Model):
    ...
    class Meta:
        ...

Django 模型的 class Meta 是个类中类,可以像普通属性一样使用它,如下所示。如果你不喜欢 Meta 这个名字,把它改个名字也行。

In [1]: class Foo(object):
   ...:     class Meta:
   ...:         metaVal = 1
   ...:         
In [2]: f1 = Foo()
In [3]: f2 = Foo()
In [4]: f1.Meta.metaVal
Out[4]: 1
In [5]: f2.Meta.metaVal = 2
In [6]: f1.Meta.metaVal
Out[6]: 2
In [7]: Foo.Meta.metaVal
Out[7]: 2

Django 的 Model 类及 ORM 是通过元类实现的,有兴趣的大家可以看看源码。

相关阅读

一文看懂 Python 系列之装饰器 (decorator)(工作面试必读)

一文看懂 Python 多进程与多线程编程 (工作学习面试必读)

一文看懂 Python 面向对象编程 (Python 学习与新手入门必看)- 绝对原创

Django 基础 (30):模型(Models) 的继承详解

https://mp.weixin.qq.com/s/fi0KS9dqqvyYLbbHr43qFQ

上次编辑于: 5/25/2021, 7:39:31 AM