zoukankan      html  css  js  c++  java
  • Python天天美味(33) 五分钟理解元类(Metaclasses)[转]

    五分钟理解元类(Metaclasses

    真的,它并非巫术。


    原文地址:http://www.voidspace.org.uk/python/articles/five-minutes.shtml

    日期:16 September, 2008.

    译者:赖勇浩(http://blog.csdn.net/lanphaday

     “元类的魔幻变化比 99% 的用户所担心的更多,当你搞不懂是否真的需要用它的时候,就是不需要。”

    —Tim Peters

    本文源于在 PyCon UK 2008 上的一个快速演讲。

    元类被称为 Python 中的“深奥的巫术”。尽管你需要用到它的地方极少(除非你基于 zope 编程),可事实上它的基础理论其实令人惊讶地易懂。

    一切皆对象

    • 一切皆对象
    • 一切都有类型
    •  class”和“type”之间本质上并无不同
    • 类也是对象
    • 它们的类型是 type

    以前,术语 type 用于内置类型,而术语 class 用于用户定义的类,但自 Pythoon 2.2 以来“class”和“type”本质上并无不同。

    对于旧风格(old-style)类的类型是 types.ClassType

    真的,这是真的

    Python 2.5.1 (r251:54869, Apr 18 2007, 22:08:04)
    >>> class Something(object):
    ...     pass
    ...
    >>> Something
    <class '__main__.Something'>
    >>> type(Something)
    <type 'type'>

    从这里可以看出在交互式解释器中创建的类是一个 first class 的对象。

    类的类是……

    它的元类……

    就像对象是类的实例一样,类是它的元类的实例。

    调用元类可以创建类。

    确切来说,Python 中的其它对象也是如此。

    因此当你创建一个类时……

    解释器会调用元类来生成它……

    定义一个继承自 object 的普通类意味着调用 type 来创建它:

    >>> help(type)

    Help on class type in module __builtin__:

     

    class type(object)

     | type(object) -> the object's type

     | type(name, bases, dict) -> a new type

    type 的第二种用法尤为重要。当 Python 解释器在执行一条类定义语句时(如例子中最初的两行代码之后),它会用下面的参数调用 type

    • 字符串形式的类名
    • 元组形式的基类序列——在我们的例子中是只有一个元素的元组(’one-pl’[1],如(object,)
    • 包括由名字影射的类成员(类属性、方法等)的字典

    简单模拟

    >>> def __init__(self):
    ...     self.message = 'Hello World'
    ...
    >>> def say_hello(self):
    ...     print self.message
    ...
    >>> attrs = {'__init__': __init__, 'say_hello': say_hello}
    >>> bases = (object,)
    >>> Hello = type('Hello', bases, attrs)
    >>> Hello
    <class '__main__.Hello'>
    >>> h = Hello()
    >>> h.say_hello()
    Hello World

    以上代码创建了类属性的字典,然后调用 type 来创建了名为 Hello 的类。

    __metaclass__ 的魔法

    只要在类定义中把 __metaclass__ 设置为任意有着与 type 相同参数的可调用对象,就能够提供自定义的元类。

    通常使用从 type 继承的方法:

    class PointlessMetaclass(type):
        
    def __new__(meta, name, bases, attrs):
            
    # do stuff...
            return type.__new__(meta, name, bases, attrs)

    重要的是在 __new__ 方法中我们能够读取或改变传入的用以创建新类的参数。从而能够内省属性字典和改动、增加或者删除成员。

    尽管当实例化一个类时这两个函数都会被调用,但覆盖 __new__ __init__ 更为重要。__init__ 初始化一个实例,而 __new__ 的职责是创建它。因此如果元类用以自定义类的创建,就需要覆盖 type __new__

    使用新类而非仅仅提供工厂函数的原因在于如果使用工厂函数(那样只是调用 type)的话元类不会被继承。

    In Action...

    >>> class WhizzBang(object):
    ...     __metaclass__ = PointlessMetaclass
    ...
    >>> WhizzBang
    <class '__main__.WhizzBang'>
    >>> type(WhizzBang)
    <class '__main__.PointlessMetaClass'>

    WhizzBang 是一个类,但它现在已经不是 type 的实例,而是我们自定义的元类的实例了……

    这有什么用?

    很好的问题,元类将用在创建使用了它的新类时调用,这里是一些关于这样做的好处的观点:

    • 装饰(Decorate)类的所有方法,用以日志记录或者性能剖分。
    • 自动 Mix-in 新方法
    • 在创建时注册类。(例如自动注册插件或从类成员创建数据库模式。)
    • 提供接口注册,功能自动发现和接口适配。
    • 类校验:防止子类化,校验所有的方法是否都有 docstrings

    最重要之处在于元类中是在最后对 type 的调用时才真正创建类,所以可以自由地随你喜欢地改变属性字典(以及名称和元组形式的基类序列)。

    一些流行的 Python ORMObject Relational Mappers(对象关系影射),用以和数据库协同工作)也如此使用元类。

    哦,还有因为元类是继承的,所以你能够提供一个使用了你的元类的基类,而继承自它的子类就无需显式声明它了。

    但是……

    我曾未需要使用它来编写代码……(我们用它来剖分,也在 Ironclad 项目广泛应用它,但我不编写这些)。

    还有,这一切只适用于 Python 2.x,其中的机制在 Python 3 中已经改变了。

    type(type) is type

    Python 2.6 中现在也可用使用 class decorators 来实现许多以前可能需要用元类来实现的东西。

    最后,还有一个极尽奇技淫巧的例子(稍为深入,但仍然不难消化),可以去看看 The Selfless Metaclass。它通过字节码和方法签名重写来避免显式地声明 self

    [1]

     'one-pl'是指只有一个元素的元组。


    Python 天天美味系列(总)

    Python 天天美味(30) - python数据结构与算法之快速排序 

    Python 天天美味(31) - python数据结构与算法之插入排序 

    Python 天天美味(32) - python数据结构与算法之堆排序 

    Python 天天美味(33) - 五分钟理解元类(Metaclasses)[转]

    Python 天天美味(34) - Decorators详解  

    ...

  • 相关阅读:
    git线上操作
    IDEA快捷方式
    Java 四种线程池
    java 获取当前天之后或之前7天日期
    如何理解AWS 网络,如何创建一个多层安全网络架构
    申请 Let's Encrypt 通配符 HTTPS 证书
    GCE 部署 ELK 7.1可视化分析 nginx
    使用 bash 脚本把 AWS EC2 数据备份到 S3
    使用 bash 脚本把 GCE 的数据备份到 GCS
    nginx 配置 https 并强制跳转(lnmp一键安装包)
  • 原文地址:https://www.cnblogs.com/coderzh/p/1349735.html
Copyright © 2011-2022 走看看