zoukankan      html  css  js  c++  java
  • Python(一)对 meta class 的理解

    1. 理解  class

    • 对于 class 来说,表示一个代码块规定了实例化后的 object 的属性和方法
    • 但是在 Python 中,class 本身也是对象。定义一个 class,就相当于在内存中实例化了一个名为 className 的对象
    • 作为一个对象,因此具备以下能力:
      • 赋值给一个变量
      • 对其拷贝
      • 作为函数参数
    class Example(object):
        pass
    
    object1 = Example()
    print(object1)
    # prints '<__main__.Example object at 0x102e26990>'
    print(Example)
    # prints '<class '__main__.Example'>'
    Example_Mirror = Example
    print(Example_Mirror)
    # prints '<class '__main__.Example'>'

    2. 动态创建 class

    • 由于 class 也是 object,因此可以像创建普通 object一样动态创建 class:
    def Dynamic_Class_Creater(name):
        if name == 'name1':
            class Class1(object):
                pass
            return Class1
        else:
            class Class2(object):
                pass
            return Class2
    
    first = Dynamic_Class_Creater('name1')
    print(first)
    # prints '<class '__main__.Class1'>'
    print(first())
    # prints '<__main__.Class1 object at 0x105452e10>'
    • 使用 class 关键字创建类的时候,Python 会自动创建对应的 object。像 Python 中其他大多数情况一样,我们也可以通过 type() 创建这个 class object
    • 通常可以通过 type 查看对象类型:
    # prints '<type 'type'>'
    print(type(Example()))
    # prints '<class '__main__.Example'>'
    • type() 函数可以接收 class 的描述来作为参数并返回所生成的 class object。type 同时具有这两个迥异的功能是由于 Python 兼容性问题导致的,不做深究
    • 通过 type 创建 class 是,用法如下:
    type(class_name, tuple_of_parent_class, dict_of_attribute_names_and_values)
    • 其中第二个参数 tuple_of_parent_class 用来表示继承关系,可以为空。第三个参数用来描述要创建的 class 所应该具有的属性:
    Example = type('Example', (), {})
    print(Example)
    <class '__main__.Example'>
    • type 所接收的第一个参数 'Example' 是该 class 的名称,同时我们使用了 Example 作为存储该 class object 引用的变量。二者可以不同,但一般不采用不同的名字从而使得代码更加复杂:
    Example = type('Example', (), {'val1': 1, 'val2': 2})
    
    # 等价于
    
    class Example(object):
        val1 = 1
        val2 = 2
    
    #####################################################
    
    ChildExample = type('ChildExample', (Example,), {})
    
    # 等价于
    
    class ChildExample(Example):
        pass
    • 添加的属性也可以是方法:
    def echo(self):
        print(self.val1)
    
    ChildExample = type('ChildExample', (Example,), {'echo': echo})
    
    print(hasattr(Example, 'echo'))
    # prints 'False'
    
    print(hasattr(ChildExample, 'echo'))
    # prints 'True'
    • 可以先创建对象,再添加属性:
    ChildExample = type('ChildExample', (Example,), {})
    
    def another(self):
        print('another')
    
    ChildExample.another = another
    • 综上所述,class 也是 object,可以实现动态创建。Python 实现动态创建主要通过 metaclass

    3. 理解 metaclass

    • 实例调用 __class__ 属性时会指向该实例对应的 class,然后可以再去调用其它类属性
    • metaclass 是 Python 中用来创建 class object 的 class,可以看做产生的 class 的工厂类:
    class = metaclass()
    object = class()
    • 所有的 class 都来自于 type。type 作为 metaclass 创建了所有 class object:
    age = 24
    print(age.__class__)
    # prints '<type 'int'>'
    print(age.__class__.__class__)
    # prints '<type 'type'>'
    • type 实际上是 Python 用在幕后创建所有 class 的 metaclass,type 是 Python 预先定义好的,也可以自定义 metaclass

    4. class 的 __metaclass__ 属性

    • 定义 class 时,可以通过 __metaclass__ 属性初始化当前 class 的 metaclass:
    class Example(object):
        __metaclass__ = something
        [other statements...]
    
    class Foo(Bar):
        pass
    • 上述代码中,Python 首先在 Foo 中寻找是否存在 __metaclass__ 属性
    • 如果存在的话,Python 将使用这个 metaclass 在内存中创建一个名字为 Foo 的 class object
    • 如果 class 定义中不存在 __metaclass__ 且没用继承任何类,Python将会寻找 MODULE 级别的 __metaclass__
    • 如果存在的话,就进行与前述相同的操作
    • 注意:只有我们定义的 class 没有继承任何类的情况下,Python才会在 MODULE 级别寻找 __metaclass__
    • 如果存在继承类且没有定义 __metaclass__ 属性,Python 会使用当前类的父类的 metaclass 来创建当前类
    • 在 class 中定义的 __metaclass__ 属性并不会被子类继承。被子类继承的是父类的 metaclass,也就是父类的 __class__ 属性,比如上面的例子中,Bar.__class__ 将会被 Foo 继承
    • 也就是说,如果Bar定义了一个 __metaclass__ 属性来创建 Bar 的 class object,那么 Bar 的子类(也就是 Foo)并不会继承这一行为

    5. 自定义 metaclass

    • 上述的 __metaclass__ 属性实际上定义的是 type 或者 type 的子类用于创建 class
    • 目的在于 class 被创建的时候对生成的 class 进行自定义修改
    • 通常用于 API 中,根据当前内容创建相匹配的 clas,比如:当前 MODULE 下的 class 的属性名称均大写
    • __metaclass__ 可以是任何 Python 的 callable,不一定是一个正式的 class 也可以是 function:
    # the metaclass will automatically get passed the same argument 
    # that is passed to `type()`
    def upper_attr(class_name, class_parents, class_attr):
        '''Return a class object, with the list of its attribute turned into 
        uppercase.
        '''
        # pick up any attribute that doesn't start with '__' and turn it into uppercase.
        uppercase_attr = {}
        for name, val in class_attr.items():
            if name.startswith('__'):
                uppercase_attr[name] = val
            else:
                uppercase_attr[name.upper()] = val
        
        # let `type` do the class creation
        return type(class_name, class_parents, uppercase_attr)
    
    
    class Foo(object):
        # this __metaclass__ will affect the creation of this new style class
        __metaclass__ = upper_attr
        bar = 'bar'
    
    
    print(hasattr(Foo), 'bar')
    # prints 'False'
    
    print(hasattr(Foo), 'BAR')
    # print 'True'
    
    f = Foo()
    print(f.BAR)
    # print 'bar'
    • 也可以通过继承 type 的方式实现一个真正的 class 形式的 metaclass:
    class UpperAttrMetaclass(type):
        def __new__(cls, cls_name, bases, attr_dict):
            uppercase_attr = {}
            for name, val in attr_dict.items():
                if name.startswith('__'):
                    uppercase_attr[name] = val
                else:
                    uppercase_attr[name.upper()] = val
            return super(UpperAttrMetaclass, cls).__new__(cls, cls_name, bases, uppercase_attr)

    6. 应用场景

    • 通常使用 metaclass 类实现一些逻辑上复杂的操作,如修改继承以及 class 的属性等操作
    • metaclass 主要的使用情况就是用来创建 API。使用 metaclass 的一个典型的例子是 Django ORM:
    class Person(models.Model):
        name = models.CharField(max_length=30)
        age = models.IntegerField()
    
    guy = Person(name='bob', age='35')
    print(guy.age)
    # prints '35'
    • 上述操作并不会返回一个 IntegerField 对象,而是会返回一个 int,甚至可以直接从数据库中调用这个值
    • 正是因为 models.Model 定义了 __metaclass__,并使用了一些操作来将我们使用简单的语句定义的 Person 转化成了与数据库相应的域相联系的类,这种逻辑才成为可能
    • Django 使得很多复杂的逻辑仅暴露一个简单的 API 接口就可以调用,这正是通过 metaclass 实现的
    • metaclass 会根据需要重新实现这些复杂操作所需要的真正的代码

    7. 参考文献

  • 相关阅读:
    查找第一个不重复的字符问题
    gops
    关于 Go 中 Map 类型和 Slice 类型的传递
    Go 程序的性能优化及 pprof 的使用
    Go语言标准库_输入/输出
    Linux 终端 Bash 常用快捷键介绍及经验
    蓄水池采样算法(Reservoir Sampling)
    Guice 依赖绑定
    基本动态规划之硬币问题
    程序打包成jar 获取不到工程目录下文件的问题
  • 原文地址:https://www.cnblogs.com/wangao1236/p/10899533.html
Copyright © 2011-2022 走看看