python中装饰器与元类的使用

一. 装饰器

        装饰器是一种设计模式, 当函数或类需要增加或修改一些功能, 但设计者又不想修改源代码, 就可以通过装饰器来实现, 装饰器可以增大代码复用程度, 有效的提高编程效率, 类似于其他语言中"宏"的使用.

        装饰器最大的特点是callable, 由于函数与类(__call__)都可以实现可调用的功能, 所以装饰器的实现方式有函数与类两种.

        装饰器的基本实现过程如下:

@staticmethod
def foo(): pass
        实际作用过程与下面代码片相同:
def foo(): pass
foo = staticmethod(foo)

        首先编译foo函数, 结果对象传入staticmethod, 然后staticmethod实现那些需要添加或修改的功能后, 返回一个function-like的对象, 并替换原来的函数. 这里的对象是一个闭包, 保存了修改后的函数与函数所在位置的环境变量(参数值等).


1. 使用类作为函数装饰器

        类具有callable性质, 通过__call__方法, 可以实现装饰器, 首先考虑最简单的情况, 对一个没有输入参数的函数的装饰器使用过程如下:

class Decorator(object):

    def __init__(self, func):
        print "The flour has been put on the table"
        self.func = func

    def __call__(self, *args, **kw):
        print "use the method '__call__'"
        return self.func(*args, **kw)

@Decorator
def func():
    print "The Office is decorated with nothing"

print "call the function: func"
func()

# The flour has been put on the table
# call the function: func
# use the method '__call__'
# The Office is decorated with nothing 

 

        1.__new__(cls, *args, **kwargs)  创建对象时调用,返回当前对象的一个实例;注意:这里的第一个参数是cls即class本身。       

        2.__init__(self, *args, **kwargs) 创建完对象后调用,对当前对象的实例的一些初始化,无返回值,即在调用__new__之后,根据返回的实例初始化;注意,这里的第一个参数是self即对象本身【注意和new的区别】。

        3.__call__(self,  *args, **kwargs) 如果类实现了这个方法,相当于把这个类型的对象当作函数来使用,相当于 重载了括号运算符。

2. 使用函数作为函数装饰器

        由于函数的callable特性, 函数做装饰器与类相似, 若被装饰函数没有输入参数:

def Decorator(func):
    print "The flour has been put on the table"

    def new_func(*args, **kw):
        return func(*args, **kw)
    return new_func


@Decorator
def func():
    print "The Office is decorated with nothing"

print "call the function: func"
func()

# The flour has been put on the table
# call the function: func
# The Office is decorated with nothing
        如果要装饰的函数带参数,
def logged(arg1, arg2, arg3):

    def wrap(func):
        def new_func(*args, **kw):
            print "The flour%s/%s/%s has been put on the table" % (arg1, arg2, arg3)
            return func(*args, **kw)
        return new_func
    return wrap


@logged('flour1', 'flour2', 'flour3')
def func():
    print "The Office is decorated with nothing"

print "call the function: func"
func()

# call the function: func
# The flourflour1/flour2/flour3 has been put on the table
# The Office is decorated with nothing
        另外@functools.wraps(func)可以防止装饰器改变原函数的__name__/__doc__等特性. 用wraps装饰器而不是手动去处理__name__或其他属性


3. 使用类装饰器

        与函数装饰器相似, 类装饰器的是输入一个类, 输出一个装饰后的类.

from functools import wraps


def make_dual(relation):
    @wraps(relation, ['__name__', '__doc__'])
    def dual(x, y):
        return relation(y, x)
    return dual


def dual_ordering(cls):
    for func in ['__lt__', '__gt__', '__ge__', '__le__']:
        if hasattr(cls, func):
            setattr(cls, func, make_dual(getattr(cls, func)))
    return cls


@dual_ordering
class rstr(str):
    pass

x = rstr("1")
y = rstr("2")
print x < y, x <= y, x > y, x >= y

# False False True True

二. 元类

        Python中, 所有的元素都是对象, 类其实是type类型的对象, 而type便是一种基础元类, 通过type可以快速创建出一些类, 特别注意第二个参数是一个tuple(父类集合):

MyClass = type("MyClass", (object,), {"my_attribute": 0})

        可以说: 类是制造对象的对象, 那么制造类的对象又是什么呢? 答案就是元类. 与装饰器类似的是, 我们可以通过callable对象(函数或者类)来定义元类.

class MyMetaClass(type):
    pass
class Foo(object):
    __metaclass__ = MyMetaClass
        python使用__metaclass__来创建类。python在创建类的时候,首先在类的定义中寻找__metaclass__,如果找到了,按照它定义的方法创建类,否则在父类中寻找,如果仍然没有找到,将进入MODULE的层级寻找,这个层级如果还没有找到,将用type创建类。

        metaclass的目的是在创建类的时候按照需要自动的添加或者改变类的内容。而metaclass本身并不必要一定是类,可以是任何可调用的对象,比如函数。首先从简单的函数开始:

def upper_attr(cls, parent, attrs):
    uppercase_attr = {}
    for name, val in attrs.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val
    return type(cls, parent, uppercase_attr)


class Foo(object):
    __metaclass__ = upper_attr
    tom = 'The boyfriend of lily'
print Foo.TOM

# the boyfriend of lily
        以上代码中元类的目的是把定义的类中非特殊属性字符串全部设置为大写,运行的结果显示这种方案简单有效。

        采用类实现Metaclass的方法与函数实现方法类似,如下:

class My_Upper_Metaclass(type):
    def __new__(cls, clsname, parent, attrs):
        uppercase_attr = {}
        for name, val in attrs.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val
        return type.__new__(cls, clsname, parent, uppercase_attr)

class Foo(object):
    __metaclass__ = My_Upper_Metaclass
    tom = 'the boyfriend of lily'
print Foo.TOM

# the boyfriend of lily
        其中__new__实现元类的方式还可以用__init__,__call__的方法实现。

        大多数情况下,元类用类来实现而不是函数,首先因为类易于扩展与继承,其次类的方法去实现复杂功能时代码可读性较高。

        元类与可以完成所有类装饰器可以完成的功能,但类装饰器较为简单,另外一段代码可以查看元类与类装饰器的执行顺序:

class my_metaclass(type):

    def __new__(cls, class_name, parents, attributes):
        print "- my_metaclass.__new__ - Creating class instance of type", cls
        return super(my_metaclass, cls).__new__(cls, class_name, parents, attributes)
    def __init__(self, class_name, parents, attributes):

        print "- my_metaclass.__init__ - Initializing the class instance", self
        super(my_metaclass, self).__init__(self)
    def __call__(self, *args, **kwargs):

        print "- my_metaclass.__call__ - Creating object of type ", self
        return super(my_metaclass, self).__call__(*args, **kwargs)

def my_class_decorator(cls):
    print "- my_class_decorator - Chance to modify the class", cls
    return cls
    
@my_class_decorator
class C(object):
    __metaclass__ = my_metaclass
    def __new__(cls):
        print "- C.__new__ - Creating object."
        return super(C, cls).__new__(cls)
    def __init__(self):
        print "- C.__init__ - Initializing object."
c = C()
print "Object c =", c

# - my_metaclass.__new__ - Creating class instance of type <class '__main__.my_metaclass'>
# - my_metaclass.__init__ - Initializing the class instance <class '__main__.C'>
# - my_class_decorator - Chance to modify the class <class '__main__.C'>
# - my_metaclass.__call__ - Creating object of type  <class '__main__.C'>
# - C.__new__ - Creating object.
# - C.__init__ - Initializing object.
# Object c = <__main__.C object at 0x7ffeeabcc8d0>
1. Python首先看类声明,准备三个传递给元类的参数。这三个参数分别为类名(class_name),父类(parent)以及属性列表(attributs)。

2. Python会检查__metaclass__属性,如果设置了此属性,它将调用metaclass,传递三个参数,并且返回一个类。

3. 在这个例子中,metaclass自身就是一个类,所以调用它的过程类似创建一个新类。这就意味着my_metaclass.__new__将首先被调用,输入四个参数,这将新建一个metaclass类的实例。然后这个实例的my_metaclass.__init__将被调用调用结果是作为一个新的类对象返回。所以此时C将被设置成这个类对象。

4. 接下来Python将查看所有装饰了此类的装饰器。在这个例子中,只有一个装饰器。Python将调用这个装饰器,将从元类哪里得到的类传递给它作为参数。然后这个类将被装饰器返回的对象所替代。

5. 当类被调用创建一个新的对象实例时,因为类的类型是metaclass,因此Python将会调用元类的__call__方法。在这个例子中,my_metaclass.__call__只是简单的调用了type.__call__,目的是创建一个传递给它的类的对象实例。

6. 下一步type.__call__通过C.__new__创建一个对象。

7. 最后type.__call__通过C.__new__返回的结果运行C.__init__。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值