博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
元类与面向切面编程
阅读量:6376 次
发布时间:2019-06-23

本文共 4446 字,大约阅读时间需要 14 分钟。

元类

在 Python中,实例对象是由类生成的,而类本身也是可以被传递和自省的对象。那么类对象是用什么创建和生成的呢?答案是元类,元类就是一种知道如何创建和管理类的对象。

让我们回顾一个内置函数type(),type不仅可以返回对象的类型,而且可以使用类名称、基类元组、类主体定义的字典作为参数来创建一个新类对象:

>>> Foo = type('Foo',(object,),{'foo':lambda self:'foo'})>>> Foo
>>> type(Foo)

实际上,新型类的默认元类就是type,类可以用__metaclass__类变量显示的指定元类,上述代码功能与下述相同:

class Foo():    __metaclass__ = type    def foo(self):        return 'foo'

如果没有显式的指定元类,class语句会检查基类元组中的第一个基类的元类,比如新型类都是继承object类的,所以新型类与object类的元类相同,为type,继承object而不显式的指定元类:

class Foo(object):    def foo(self):        return 'foo'

如果没有指定基类,class语句会检查全局变量__metaclass__,如果没有找到__metaclass__值,Python会使用默认的元类。

在python 2中,默认的元类是types.ClassType,就是所谓的旧样式类。python2.2以后已不提倡使用,比如不指定元类并且不继承object基类:

class Foo():    def foo(self):        return 'foo'
>>> import types>>> isinstance(Foo, types.ClassType)True

python 3以后,默认的元类皆为type了,显式定义元类的时候需要在基类元组中提供metaclass关键字,class Foo(metaclass=type)如此定义。

 使用元类的时候,一般会自定义一个继承自type的子类,并重新实现__init__()与__new__()方法:

class ExampleType(type):    def __new__(cls, name, bases, dct):        print 'create class %s'%name        return type.__new__(cls, name, bases, dct)    def __init__(cls, name, bases, dct):        print 'Init class %s'%name        type.__init__(cls, name, bases, dct)class Foo(object):    __metaclass__ = ExampleType>>> create class FooInit class Foo>>> Foo

可见,使用class语句定义类后,元类就使用传递给元类的类名称、基类元组和类方法字典创建类。

因为元类创建的实例是类对象,所以__init__方法的第一个参数按惯例写为cls,其实与self功能相同。

面向切面编程

在运行时,动态地将代码切入到类的指定方法、指定位置上的编程称为面向切面的编程(AOP)。

简单地说,如果不同的类要实现相同的功能,可以将其中相同的代码提取到一个切片中,等到需要时再切入到对象中去。这些相同的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。

比如,要为每个类方法记录日志,在python中一个可行的方法是使用装饰器:

def trace(func):    def callfunc(self, *args, **kwargs):        debug_log = open('debug_log.txt', 'a')        debug_log.write('Calling %s: %s ,%s\n'%(func.__name__, args, kwargs))        result = func(self, *args, **kwargs)        debug_log.write('%s returned %s\n'%(func.__name__, result))        debug_log.close()        return result    return callfuncdef logcls(cls):    for k, v in cls.__dict__.items():        if k.startswith('__'):            continue        if not callable(v):            continue        setattr(cls, k, trace(v))    return cls@logclsclass Foo(object):    num = 0    def spam(self):        Foo.num += 1        return Foo.num

另外一个可行的方法就是使用元类了:

def trace(func):    def callfunc(self, *args, **kwargs):        debug_log = open('debug_log.txt', 'a')        debug_log.write('Calling %s: %s ,%s\n'%(func.__name__, args, kwargs))        result = func(self, *args, **kwargs)        debug_log.write('%s returned %s\n'%(func.__name__, result))        debug_log.close()        return result    return callfuncclass LogMeta(type):    def __new__(cls, name, bases, dct):        for k, v in dct.items():            if k.startswith('__'):                continue            if not callable(v):                continue            dct[k] = trace(v)        return type.__new__(cls, name, bases, dct)class Foo(object):    __metaclass__ = LogMeta    num = 0    def spam(self):        Foo.num += 1        return Foo.num

元类的一个主要用途就是检查收集或者更改类定义的内容,包括类属性、类方法、描述符等等。

元类与基类

元类中除了可以定义__init__和__new__方法外,还可以定义其它的属性和方法:

class ExaMeta(type):    name = 'ExaMeta'    def get_cls_name(cls):        print cls.__name__class Foo(object):    __metaclass__ = ExaMeta

那么,类可不可以访问元类定义的方法和属性呢?

>>> Foo.get_cls_name()Foo>>> Foo.name'ExaMeta'

这很好理解,类Foo是元类的一个实例,在实例的__dict__中查找不到要查询的属性时,就会到实例所属的类字典中去查找,而元类正是定义类Foo的类。

可以再尝试下使用类Foo的实例去访问元类的属性或者方法:

>>> Foo().get_cls_name()AttributeError: 'Foo' object has no attribute 'get_cls_name'>>> Foo().nameAttributeError: 'Foo' object has no attribute 'name'

显然不能访问。

查找一个不与实例关联的属性时,即先在实例的类中查找,然后再在从所有的基类中查找,查找的顺序可以用__mro__属性查看:

>>> Foo.__mro__(
,
)

元类并不在其中,毕竟,类与元类不是继承关系,而是实例与类的创造关系。

元类属性的可用性是不会传递的,也就是说,元类的属性是对它的类实例是可用的,但是对它的类实例的实例是不可用的,这正是元类与基类的主要不同。

有时候,一个类会同时有元类和基类:

class M(type):    name = 'M'class B(object):    name = 'B'class A(B):    __metaclass__ = M

属性访问是这样的:

>>> A.name'B'>>> A().name'B'

可见类会先到继承的基类中去查找属性。

元类冲突

假如有两个不同元类的类,要生成一个继承这两个类的子类,会产生什么情况呢?

class MA(type):    passclass A(object):    __metaclass__ = MAclass MB(type):    passclass B(object):    __metaclass__ = MBclass C(A, B):    pass

结果会报错,提示元类冲突:

TypeError: Error when calling the metaclass bases    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

我们需要手动构造新子类的元类,让新子类的元类继承自A和B的元类:

class MC(MA, MB):    passclass C(A, B):    __metaclass__ = MC

  

转载地址:http://tmtqa.baihongyu.com/

你可能感兴趣的文章
java菜单栏不下拉_我java代码中的下拉列表设好后为什么无法下拉?
查看>>
java传递引用类型的实质_java的引用类型以及值传递
查看>>
java策略模式使用场景,Java设计模式—策略模式
查看>>
RHEL6.3实现基于加密的用户认证验证访问
查看>>
SCCM2012 R2实战系列之十一:解决OSD分发Windows7 系统盘盘符为’D’问题
查看>>
经验分享:我是如何在网店无货源情况下快速出单?
查看>>
当AD服务器置于防火墙内时,所需开放的端口
查看>>
限免的Mac App套件,工程师绝对不可错过
查看>>
Exchange 2013 添加地址列表到脱机通讯簿
查看>>
Skype for Business Server 2015-05-监控和存档服务器-配置
查看>>
浅谈物化视图
查看>>
安装SQL Server 2017
查看>>
超融合超越企业传统存储绕不开的六个问题
查看>>
医院CIO的一幅工作对联
查看>>
DPM灾难切换应用场景
查看>>
简单配置Oracle10g DataGuard物理备库
查看>>
网曝支付宝漏洞:手机丢了,支付宝也就完了
查看>>
4 在vCenter Server安装View Composer组件
查看>>
SFB 项目经验-24-为持久聊天室-查询或者增加成员
查看>>
Linux下配置Squid基础教程
查看>>