Windless
订阅/Feed
稗田千秋(i@wind.moe)

Python元编程 元类篇

稗田千秋
Apr.10 2017 code

前文 Python元编程 装饰器篇

这个问题是我重构博客后台时遇到的,在 constant.py 里创建了一个全局变量 CATEGORY,但是由于作用域的问题在引用时屡屡失败,最后便考虑使用单例类来作为存储结构,最先访问时会创建新实例,否则返回该实例。

0x00 什么是元类

首先,先来看看type的用法

> help(type)
Help on class type in module builtins:

class type(object)
 |  type(object_or_name, bases, dict)
 |  type(object) -> the object's type  # type(对象)
 |  type(name, bases, dict) -> a new type # type(类名, 父类元组,方法字典)

第二个是普通的检测对象类型的函数,重点是第三条,我们来创建一个新类

class NewClass(Foo):
    def echo(self):
          print('how r u')

#上面是常用方法,根据type的提示,等同于如下
def echo(self):
    print('who am i')

NewClass = type('NewClass', (Foo,), {'echo':echo})

所以,元类归根结底,就是类的类,即在上文的 type 外再包一层,我们将对象称为类的实例,那么对应的,类可以称为元类的实例,调用元类用来创建类。

0x01 元类的使用

元类的目的即是在创建类的时候能够改变类的某些特性,一个常见的例子,下面是某Django后端的models.py文件

class Article(models.Model):
    """ 文章"""
    title = models.CharField(max_length=200)
    desc = models.CharField(max_length=50)

在使用的时候

article = Article.objects.get(pk=kwargs["pk"])
print(article.id)

返回的并不是 models.CharField 对象,而是具体的数据,使用PyCharm跟踪,可以发现Django的Model类使用了元类

class Model(six.with_metaclass(ModelBase)):

继续追踪,ModelBase 在__new__方法里有近余百行的操作来简化Model的操作,这是是一个使用元类的绝佳例子,用户无需关心发生了什么,只需要用就够了。

0x02 示例,实现单例结果

正如文章开头说的,为了解决 CATEGORY 作为变量供全局调用,最后选择了单例类,先来看单例类的代码

class Singleton(type):
    def __init__(cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls._instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
            return cls._instance
        else:
            return cls._instance

代码相对简单,当类初始化的时候创建一个 _instance 实例,当调用的时候判断cls._instance ,如果别处已经实例化过了,则返回该实例化的对象。调用就十分简单了

class Const(metaclass=Singleton):
    def __init__(self):
        self.CATEGORY = []

只需将 metaclass 设为 Singleton 即可,这样当 Const 被调用的时候,便会创建一个 Const 实例并缓存在其中,便能变相地作为一个全局变量使用啦~

引用

--END--
文章创建于 2017-04-10 14:47:29,最后更新 2017-04-14 12:52:43
Comment
尝试加载Disqus评论, 失败则会使用基础模式.
    • play_arrow

    About this site

    version:1.02 Alpha
    博客主题: Lime
    联系方式: i@wind.moe
    写作语言: zh_CN & en_US
    博客遵循 CC BY-NC-SA 4.0许可进行创作

    此外,本博客会基于访客的Request Headers记录部分匿名数据用于统计(Logger的源码见Github),包含Referer, User-Agent & IP Address.个人绝不会主动将数据泄露给第三方