专栏名称: 编程派
Python程序员都在看的公众号,跟着编程派一起学习Python,看最新国外教程和资源!
目录
相关文章推荐
51好读  ›  专栏  ›  编程派

进阶 | Python 面向切面编程与元类

编程派  · 公众号  · Python  · 2017-12-21 11:30

正文

作者:再见紫罗兰

来源:http://www.cnblogs.com/linxiyue/p/8030604.html

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

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

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

  2. >>> Foo

  3. >>> type(Foo)

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

  1. class Foo():

  2.    __metaclass__ = type

  3.    def foo(self):

  4.        return 'foo'

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

  1. class Foo(object):

  2.    def foo(self):

  3.        return 'foo'

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

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

  1. class Foo():

  2.    def foo(self):

  3.        return 'foo'

  4. >>> import types

  5. >>> isinstance(Foo, types.ClassType)

  6. True

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

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

  1. class ExampleType(type):

  2.    def __new__(cls, name, bases, dct):

  3.        print 'create class %s'%name

  4.        return type.__new__(cls, name, bases, dct)

  5.    def __init__(cls, name, bases, dct):

  6.        print 'Init class %s'%name

  7.        type.__init__(cls, name, bases, dct)

  8. class Foo(object):

  9.    __metaclass__ = ExampleType

  10. >>>

  11. create class Foo

  12. Init class Foo

  13. >>> Foo

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

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

面向切面编程

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

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

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

  1. def trace(func):

  2.    def callfunc(self, *args, **kwargs):

  3.        debug_log = open('debug_log.txt', 'a')

  4.        debug_log.write('Calling %s: %s ,%sn'%(func.__name__, args, kwargs))

  5.        result = func(self, *args, **kwargs)

  6.        debug_log.write('%s returned %sn'%(func.__name__, result))

  7.        debug_log.close()

  8.        return result

  9.    return callfunc

  10. def logcls(cls):

  11.    for k, v in cls.__dict__.items():

  12.        if k.startswith('__'):

  13.            continue

  14.        if not callable(v):

  15.            continue

  16.        setattr(cls, k, trace(v))

  17.    return cls

  18. @logcls

  19. class Foo(object):

  20.    num = 0

  21.    def spam(self):

  22.        Foo.num += 1

  23.        return Foo.num

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

  1. def trace(func):

  2.    def callfunc(self, *args, **kwargs):

  3.        debug_log = open('debug_log.txt', 'a')

  4.        debug_log.write('Calling %s: %s ,%sn'%(func.__name__,







请到「今天看啥」查看全文


推荐文章
爱手工  ·  21号晚开课啦:微纸雕-主红鸟
8 年前
悦网美文日赏  ·  我拿你当朋友,你把我当人脉
8 年前
抢先电影院  ·  赏色 | 林熙蕾性感写真
8 年前