zoukankan      html  css  js  c++  java
  • 【Python语法进阶】元类--类的类

    参考链接:Python基础:元类 - RussellLuo - 博客园 (cnblogs.com)oop - What are metaclasses in Python? - Stack Overflow

    元类(metaclass)是Python 2.2中引入的概念,利用元类可以 定制类的创建行为Customizing class creation),是定义类的类。

    1、类的创建过程

    对于类定义:
    class Foo(Base):
        def say(self):
            print 'hello'
    Python解释器执行class语句 时,处理步骤如下:
    1. 确定元类mcls。元类的查找优先级为:
      • 首先查找类Foo是否拥有属性__metaclass__
      • 否则查找类Foo的父类是否具有属性__metaclass__
      • 否则查找类Foo所在模块是否具有全局变量__metaclass__
      • 否则使用默认元类(经典类:types.ClassType;新式类:type
    2. 使用元类mcls创建类Foo。创建语意等价于:
      # 元类的初始化参数:mcls(name, bases, dict)
      # 其中,name是待生成类的名字,bases是所有父类的列表,dict是待生成类的所有属性对应的字典
      Foo = mcls('Foo', (Base,), {'say': say})
    3. 创建成功后,类Foo是元类mcls实例
    综上:创建类其实是一种更高级别的实例化过程,本质上与创建类实例 相似(注意,元类调用过程在执行class语句(创建class)时,故远在执行__Init__函数(创建实例)之前)
    # 创建类:
    #     类:  mcls
    #     实例:Foo
    class Foo: pass
    # 等价于
    Foo = mcls('Foo', (), {})
    
    # 创建类的实例
    #     类:  Foo
    #     实例:fooObj
    foo = Foo()

    2、元类的使用惯例

    原则上,元类可以是:任何接受参数name,bases,dict并返回可调用对象(类或函数,参考metaclass)。其中,
    • name:待创建类的名字
    • bases:待创建类的父类(注意,不包含更深的祖先类)
    • dict:待创建类的所有attributes的名字和handle
    例如元类可以是函数
    def metacls_func(name, bases, dict):
        # do customizing here
        return type(name, bases, dict)
    更好的习惯是使用作为元类,示例如下:
    class MetaCls(type):
        def __new__(cls, name, bases, dict):
            # do customizing here: 下面语句对应使用默认的元类type生成类(cls实际上是MetaCls的self)
            return super(MetaCls, cls).__new__(cls, name, bases, dict)

    注意:

    • 元类可以继承自另一个元类,也可以使用其他元类

    • 除了常用的__new__,还可以借助__init____call__来定制被创建的类

    3、案例

    默认类生成行为

    以新式类(New-style classes)为例:类New的三种等价定义
    class New(object): pass
    
    class New:
        __metaclass__ = type
    
    New = type('New', (), {})

    New是元类type的实例:

    >>> isinstance(New, type)
    True

    使用元类

    为所有类打上作者标签:
    class AuthorTag(type):
        def __new__(cls, name, bases, dict):
            dict['__author__'] = 'RussellLuo'
            return super(AuthorTag, cls).__new__(cls, name, bases, dict)
    
    class MyBlog:
        __metaclass__ = AuthorTag
    
    class MyGitHub:
        __metaclass__ = AuthorTag
    现在,类MyBlog和类MyGitHub都获得了作者签名:
    >>> MyBlog.__author__
    'RussellLuo'
    >>> MyGitHub.__author__
    'RussellLuo'

    使用metacalss的方式有多种,具体列举如下几种:

    # python 2
    class MyBlog:
        __metaclass__ = AuthorTag
    
    # python 3
    class MyBlog(metaclass = AuthorTag):
        pass
    
    # both python 2 & 3
    import six
    @six.add_metaclass(Meta)
    class MyBlog(object):
        pass
    其中,six模块是Python为了兼容Python 2.x 和Python 3.x提供的一个模块,该模块中有一个针对类的装饰器 @six.add_metaclass(MetaClass)可以为两个版本的Python类方便地添加metaclass。

    进阶案例

    下面的案例,利用metaclass实现了为类的所有attribute function批量添加wrapper函数的功能(参考原链接python3利用元类批量给所有继承类增加装饰器_这天鹅肉有毒-CSDNimport six

    import types
    import inspect
    from functools import wraps
    from collections import OrderedDict
    
    def xx(func):
        @wraps(func)
        def wrap(*args,**kwargs):
            print('*'*150)
            print('执行了decorator')
            res=func(*args,**kwargs)
            if res:
                return res
            print('执行完毕')
            print('*'*150)
        return wrap
    
    
    class Meta(type):
    
        @classmethod
        def options(cls,bases,attrs):
    
            def _add_wrapper(value, key):
                 if hasattr(value,'__func__') or isinstance(value,types.FunctionType):
                     if isinstance(value,staticmethod):
                         return staticmethod(cls.func(value.__func__))
                     elif isinstance(value,classmethod):
                         return classmethod(cls.func(value.__func__))
                     elif isinstance(value, property):
                         return property(fget=cls.func(value.fget), fset=cls.func(value.fset), fdel=cls.func(value.fdel),doc=value.__doc__)
                     elif not key.startswith('__') :
                         return cls.func(value)
                 return value
    
            new_attrs=OrderedDict() #循环自己的所有属性
            for key,value in attrs.items(): #对各种类型的方法进行分别处理 
                new_attrs[key]=_add_wrapper(value, key)
    
            # 循环所有父类
            for base in bases:
                for key,value in base.__dict__.items():
                    if key not in new_attrs:
                        new_attrs[key]=_add_wrapper(value, key)
    
            return new_attrs
    
        def __new__(cls, name, bases, attrs):
    
            cls.func=attrs.get('meta_decoator')
            assert inspect.isfunction(cls.func), ('传入的meta装饰器不正确')
            #在类生成的时候添加wrapper
            new_attrs = cls.options(bases, attrs)
            return super().__new__(cls,name,bases, new_attrs)
            
    class obj(object):
    
        def __init__(self):
            print('obj.__init__')
    
        @classmethod
        def one(self):
            print('obj.one')
    
    @six.add_metaclass(Meta)
    class obj1(obj):
        #只要继承类中有meta_decoator属性,这个属性的方法就会自动装饰下面所有的方法
        #包括类属性,实例属性,property属性,静态属性
        meta_decoator=xx
        aa=1
        @classmethod
        def three(cls):
            print('obj1.three')
    
        @staticmethod
        def four():
            print('obj1.four')
    
        def two(self):
            print(self.pro)
            print('obj1.two')
    
        @property
        def pro(self):
            return 1

     完结,撒花~

  • 相关阅读:
    SpringMVC什么时候配置 视图解析器
    打印数组的5种方式
    集合区别(list和linkedlist的区别)?
    回归测试
    dom4j组装xml 以及解析xml
    java操作文件创建、删除
    powerdesigner里的table背景色是不是可以修改的?
    如何设定editplus为txt默认打开程序?
    PowerDesigner怎样才能在修改表的字段Name的时候Code不自动跟着变
    PowerDesigner怎么调出工具箱?
  • 原文地址:https://www.cnblogs.com/Xiaoyan-Li/p/14761129.html
Copyright © 2011-2022 走看看