zoukankan      html  css  js  c++  java
  • python中的__metaclass__

    什么是元类:

      python中类也是一种对象, 可以称为类对象.

      元类就是用来创建类对象的"东西". 你创建类就是为了创建类的实例对象, 不是吗? 但是我们已经学习了python中的类也是对象. 元类就是用来创建这些类对象的, 元类就是类的类, 你可以这样理解:

    MyClass = MetaClass()
    MyObject = MyClass()

    你已经看到了type可以这样来动态的创建类:

    MyClass = type("MyClass", (), {})

    这是因为type实际上是一个元类. type就是python在背后用来创建所有类的元类. 那么现在你肯定特别想知道type既然是一个类(元类), 为什么首字母没有大写? 好吧, 我猜这是为了与int, str, function这些python内置数据类型保持一致, str是用来创建字符串对象的类, 而int是用来创建整数对象的类, type就是用来创建类对象的类. 你可以通过检查__class__属性来证明这一点. python中所有的东西都是对象(这一点和javascript相同), 这包括整数, 字符串, 函数以及类, 它们全部都是对象, 而且它们都是从一个类创建而来, 这个类就是type.

    >>> age = 35
    >>> age.__class__
    <type 'int'>
    >>> name = 'bob'
    >>> name.__class__
    <type 'str'>
    >>> def foo(): pass
    ... 
    >>> foo.__class__
    <type 'function'>
    >>> class Bar(object): pass
    ... 
    >>> b = Bar()
    >>> b.__class__
    <class '__main__.Bar'>

    看一下上面这些任何一个__class__的__class__属性又是什么呢?

    >>> age.__class__.__class__
    <type 'type'>
    >>> name.__class__.__class__
    <type 'type'>
    >>> foo.__class__.__class__
    <type 'type'>
    >>> b.__class__.__class__
    <type 'type'>
    >>> Bar.__class__
    <type 'type'>

    从上面结果可以看出, python中的所有普通的类也是对象, 当然这些类对象也有类型. 可以看出, int, str, 函数, 普通自定义类, 这些类对象的类型都是type.

      元类就是创建类对象的类. 如果你喜欢, 可以把元类看做"类工厂". type就是python的内建元类, 当然也可以创建自己的元类.

    __metaclass__属性:

      你可以在实现一个类的时候为其添加__metaclass__属性:

    class Foo(object):
        __metaclass__ = something
        ...

    如果你这么做了, python就会用元类来创建类Foo. 当在内存中创建类对象Foo时, python会在类的定义中寻找__metaclass__属性, 如果找到了, python就用它来创建Foo, 如果没有找到, 就会用内建的type元类来创建这个类.

    当你实现一个有继承的类时:

    class Foo(Bar):
        pass

    python会做如下操作:

    Foo中有__metaclass__这个属性吗? 如果是, python会在内存中通过__metaclass__创建一个名字为Foo的类对象. 如果python没有找到__metaclass__, 它会继续在Bar(父类)中寻找__metaclass__, 以此类推. 如果python在任何父类中都没有找到__metaclass__, 它就会在模块层次中寻找__metaclass__. 如果还是找不到__metaclass__, python就会用内置的type元类来创建这个类对象.

    自定义元类:

      元类的主要目的是为了当创建类时能够自动的改变类, 或者说定制类. 为什么这么说呢, 我们可以这样理解: 把类的创建过成想象成一个管道, 如果遍历完继承链+模块层次都没有发现__metaclass__属性, 那么用type元类来创建该类. 这种情况下我们在类中怎样定义属性, 最终生成的类对象也会具有怎样的属性, 如下图所示:

    类创建时的样子---(type)-->类最终的样子(由于是用type元类, 所以类最终样式与类创建时样式一样)

    如果遍历过程中找到了__metaclass__属性, 那么就会用自定义元类来创建该类, 如下图所示:

    类创建时的样子---(__metaclass__属性指向的元类)--->类最终的样子

    由于在我们自定义的元类里面可以定制那些__metaclass__指过来的类, 因此元类的主要目的一般是为了当创建类时自动的改变类, 或者说定制类.

      到底什么时候才会用到元类呢? 通常你会为API做这样的事情, 你希望可以创建复合当前上下文的类. 假想一个很傻的例子, 你希望在你的模块里所有类的属性都应该是大写形式. 有好几种方法可以办到, 但其中一种就是通过在模块级别设定__metaclass__. 采用这种方法, 这个模块中的所有类都会通过这个元类来创建, 我们只需要告诉元类把所有的属性都改成大写形式就行:

    # !/usr/bin/python
    # -*- coding: utf-8 -*-
    # type实际上是一个类, 就像str和int一样, 所以你可以从type继承
    class UpperAttrMetaClass(type):
        def __new__(cls, name, bases, dct):
            attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
            uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
            return type.__new__(cls, name, bases, uppercase_attrs)
    
    class Foo(object):
        __metaclass__ = UpperAttrMetaClass
        bar = 'bip'
    
    print hasattr(Foo, 'bar')
    # 输出false
    print hasattr(Foo, 'BAR')
    # 输出true
    
    f = Foo()
    print f.BAR
    # 输出bip

    也可以使用super方法:

    class UpperAttrMetaClass(type):
        def __new__(cls, name, bases, dct):
            attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
            uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
            #return type.__new__(cls, name, bases, uppercase_attrs)
            return super(UpperAttrMetaClass, cls).__new__(cls, name, bases, uppercase_attrs)

    就是这样, 除此之外关于元类真的没有别的可以说的了. 就元类本身而言, 它其实很简单:

    1. 拦截类的创建

    2. 修改类(或者说定制类)

    3. 返回修改之后的类

    究竟为什么要使用元类:

    元类的主要作用是创建API. 一个典型的例子是Django ORM. 它允许你像这样定义类:

    class Person(models.Model):
        name = models.CharField(max_length = 30)
        age = models.IntegerField()

     但是如果你像这样做的话:

    guy = Person(name = "bob", age = "35")
    print guy.age

    调用guy.age并不会返回一个IntegerField对象, 而是会返回一个int, 是指可以直接从数据库中取出数据. 这是有可能的, 因为models.Model定义了__metaclass__, 并且在元类中将你刚刚定义的简单的Person类给修改了, 变的能够访问数据库了. Django框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的API(models.Model)将其化简, 通过这个API重新创建代码, 在背后完成真正的工作.

    结语:

      现在我们知道了, 类本身也是实例, 只不过它们是元类的实例. python中的一切都是对象, 这些对象要么是类对象的实例, 要么是元类的实例, 除了type. 元类是比较复杂的, 大多数情况下, 我们不会对类做修改. 如果需要对类做修改,  大多数情况下我们会通过下面两种技术来动态修改类:

    1. Monkey patching

    2. class decorators

  • 相关阅读:
    表的转置 行转列: DECODE(Oracle) 和 CASE WHEN 的异同点
    Sql中EXISTS与IN的使用及效率
    Oracle学习之start with...connect by子句的用法
    Java复制、移动和删除文件
    简单的实现微信获取openid
    SQL语句中LEFT JOIN、JOIN、INNER JOIN、RIGHT JOIN的区别?
    java 基础最全网站
    SpringBoot(十一)过滤器和拦截器
    做项目遇到的问题集锦
    使用Java实现二叉树的添加,删除,获取以及遍历
  • 原文地址:https://www.cnblogs.com/iamswf/p/4713749.html
Copyright © 2011-2022 走看看