zoukankan      html  css  js  c++  java
  • [TimLinux] Python 再谈元类 metaclass

    本博文通过对以下链接进行理解后,编写。

    https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python

    1. 类

    类也是对象,具有对象的特点:

    • 你可以将它赋值给变量
    • 你可以copy它
    • 你可以给它添加属性
    • 你可以把它作为函数参数进行传递

    类定义语法:

    class Foo(object): pass
    
    说明:
    1. 这是一种语法糖,一种对类对象的声明方式,与def func, a = 123, 本质上没有区别,都是为了创建一个对象。
    2. 对象的创建都关联到一个类,比如 a = 123 关联的类是 'int', b = 'hello' 关联的类是 'str', def func(): pass 关联的类是 'function', 而 class Foo(object): pass 关联的类就是元类 'metaclass', 默认为 'type'

    对象重要特性:动态创建,你可以随时随地在你需要的地方创建对象,类也可以动态创建。

    2. 动态创建

    根据函数参数来创建类,看起来像是类工厂,但是动态性不够。

    def choose_class(name):
        if name == 'foo':
            class Foo(object): pass
            return Foo # return the class, not an instance
        else:
            class Bar(object): pass
            return Bar
    
    >>> MyClass = choose_class('foo')
    >>> print(MyClass) # the function returns a class, not an instance
    <class '__main__.Foo'>
    >>> print(MyClass()) # you can create an object from this class
    <__main__.Foo object at 0x89c6d4c>

    真正的动态创建,是通过元类(默认type类构造函数)来创建:

    # 创建类
    >>> Foo = type('Foo', (), {'bar':True})
    >>> print(Foo)
    <class '__main__.Foo'>
    >>> print(Foo.bar)
    True
    >>> f = Foo()
    >>> print(f)
    <__main__.Foo object at 0x8a9b84c>
    >>> print(f.bar)
    True
    
    # 创建子类
    >>> FooChild = type('FooChild', (Foo,), {})
    >>> print(FooChild)
    <class '__main__.FooChild'>
    >>> print(FooChild.bar) # bar is inherited from Foo
    True
    
    # 给子类分配实例方法
    >>> def echo_bar(self):
    ...       print(self.bar)
    ...
    >>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
    >>> hasattr(Foo, 'echo_bar')
    False
    >>> hasattr(FooChild, 'echo_bar')
    True
    >>> my_foo = FooChild()
    >>> my_foo.echo_bar()
    True
    
    # 再分配一个实例方法
    >>> def echo_bar_more(self):
    ...       print('yet another method')
    ...
    >>> FooChild.echo_bar_more = echo_bar_more
    >>> hasattr(FooChild, 'echo_bar_more')
    True

    3. 动态创建语法糖

    为此 python 语言本身又提供了一个语法糖,在声明类的时候,就指定后台自动创建这个类的方法,方法就是 __metaclass__ 变量(python2.7), metaclass=xxx(python3.x)。示例:

    # 文件保存为:mymetaclass.py
    # 执行 python2 metaclass.py
    
    def upper_attr(name, parents, attributes):
        new_attributes = {}
        for n, v in attributes.items():
            if not n.startswith('__'):
                uppercase_attr[n.upper()] = v
            else:
                uppercase_attr[n] = v
        return type(name, parents, new_attributes)
    
    __metaclass__ = upper_attr  # py3这个无法工作
    
    class Foo(): # py3,需要使用这个语法:class Foo(metaclass=upper_attr):
        bar = 'bip'
    
        def __init__(self):
            self.link = "link"
        def func(self):
            print("Enter func")
    
    # 类属性,函数都被转换为大写了
    print(hasattr(Foo, 'bar'))  # False
    print(hasattr(Foo, 'BAR'))  # True
    
    print(hasattr(Foo, 'func'))  # False
    print(hasattr(Foo, 'FUNC'))  # True
    
    # 元类转换了类属性,但是没有转换实例属性, __init__ 内的变量没有被转换
    print(hasattr(Foo, 'link'))  # False
    print(hasattr(Foo, 'LINK'))  # False
    
    print(hasattr(Foo(), 'link'))  # True
    print(hasattr(Foo(), 'LINK'))  # False

     以上使用的是函数作为元类实现方式,也可以直接使用类作为元类,元类中实现的是 __new__ 方法,在生成类对象之前会被调用的方法即为 __new__ 方法。而不是 __init__ 方法, __init__ 方法是操作的类对象返回之后,创建的实例对象的初始化行为。

    # metaclass 控制的是 __new__ 方法,而不是 __init__ 方法
    class UpperAttrMetaclass(type):
        def __new__(cls, name, parents, attributes):
            new_attributes = {}
            for n, v in attributes.items():
                if not n.startswith('__'):
                    new_attributes[n.upper()] = v
                else:
                    new_attributes[n] = v
            #以下返回值推荐使用super方法,对当前示例两者效果相同,使用super可以使
            #当前元类能够继承其他元类
            #return type.__new__(cls, name, parents, new_attributes)
            #return super(UpperAttrMetaclass, cls).__new__(cls, name, parents, new_attributes)
            return type.__new__(cls, name, parents, new_attributes)
    
    class Foo(object, metaclass=upper_attr):
        bar = 'bip'
        def __init__(self):
            self.link = "link"
        def func(self):
            print("Enter func")
    
    # 效果与使用函数的元类实现一致!!!
    # 类属性,函数都被转换为大写了
    print(hasattr(Foo, 'bar'))  # False
    print(hasattr(Foo, 'BAR'))  # True
    
    print(hasattr(Foo, 'func'))  # False
    print(hasattr(Foo, 'FUNC'))  # True
    
    # 元类转换了类属性,但是没有转换实例属性, __init__ 内的变量没有被转换
    print(hasattr(Foo, 'link'))  # False
    print(hasattr(Foo, 'LINK'))  # False
    
    print(hasattr(Foo(), 'link'))  # True
    print(hasattr(Foo(), 'LINK'))  # False

    4. 写作最后

    99%用户用不到元类,所以大家洗洗睡吧,了解就行。

  • 相关阅读:
    数据库周刊33丨腾讯Tbase新版本发布;“2020数据技术嘉年华”有奖话题遴选;阿里云技术面试题;APEX 实现数据库自动巡检;MYSQL OCP题库……
    常用ASCII码对照表
    linux 环境root用户新建用户和删除用户
    Utl_Raw.Cast_To_Raw(dbms_obfuscation_toolkit.md5())
    months_between()
    GREATEST(),ROUND(),
    TRUNC()
    oracle+function
    oracle存储过程----遍历游标的方法(for、fetch、while)
    oracle+seqTest
  • 原文地址:https://www.cnblogs.com/timlinux/p/9698729.html
Copyright © 2011-2022 走看看