【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

 

 完结,撒花~

posted @ 2021-05-12 18:39  TinaSmile  阅读(168)  评论(0编辑  收藏  举报