python装饰器和描述器的使用总结

原文出处: quietin   

被某些中文教程坑过,我的建议是有问题看官方文档,即使没有很详细的例子,至少不坑

装饰器

毫无疑问在python中用得非常多

1
2
3
4
5
6
7
8
9
10
def deco(func):
    def _deco():
        print ‘before invoked’
        func()
        print ‘after invoked’
    return _deco
 
@deco
def f():
    print ‘f is invoked’


f上加deco装饰器相当于f = deco(f), 和functools.partial有些类似

如果被装饰的函数f带参数且有返回值

1
2
3
4
5
6
7
8
9
10
11
12
def deco(func):
    def _deco(*args, **kwargs):
        print ‘before invoked’
        ret = func(*args, **kwargs)
        print ‘after invoded’
        return ret
    return _deco
 
@deco
def f(a):
    print ‘f is invoked’
    return a + 1

如果装饰器带有参数,需要多包一层,把参数调用包进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def deco(*args):
    def _deco(func):
        def __deco(*args, **kwargs):
            print ‘decorator args is’, args
            print ‘before invoked’
            ret = func(*args, **kwargs)
            print ‘after invoded’
            return ret
        return __deco
    return _deco
  
@deco(‘test’)
def f(a):
    print ‘f is invoked’
    return a + 1

只有最里面一层的__deco才会每次都调用,其它外层函数只在包装时调用一次,当然,你可以在其中声明变量,然后拿到__deco里使用。如果需要保留函数名,则在__deco上加@functools.wraps装饰器

使用 作装饰器,注意是此时相当于装饰函数,被装饰的函数会作为实例化参数,得到一个类实例,以python wiki上一个做登录检查的代码为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class LoginCheck:
    def __init__(self, f):
        self._f = f
 
    def __call__(self, *args):
        Status = check_function()
        if Status is 1:
            return self._f(*args)
        else:
            return alt_function()
 
 
def check_function():
    return test
 
 
def alt_function():
    return ‘Sorry – this is the forced behaviour’
 
 
@LoginCheck
def display_members_page():
    print ‘This is the members page’

描述器

描述器在监视特定属性的时候很有用,其只在新式类中起作用。所有的描述器协议如下:

descr.__get__(self, obj, type=None) --> value
descr.__set__(self, obj, value) --> None
descr.__delete__(self, obj) --> None

如果一个对象同时定义了 __get__()__set__(),它叫做资料描述器(data descriptor)。仅定义了 __get__() 的描述器叫非资料描述器
描述器在属性访问时被自动调用。举例来说, obj.x 会在 obj 的字典中找x ,如果x定义了 __get__方法,那么 x.__get__(obj)会依据下面的优先规则被调用

调用优先级:
资料描述器 -> 实例字典 -> 非资料描述器

常用的描述器就是property了,一般都只实现了__get__的接口
先给出一个classmethod的实现和一个用于测试描述器优先级的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class classproperty(object):
    def __init__(self, func):
        self.func = func
 
    def __get__(self, instance, owner):
        return self.func(owner)
 
 
class MyClass(object):
 
    @classproperty
    def name(cls):
        return cls.__name__
 
    @property
    def x(self):
        return self._data
 
    @x.setter
    def x(self, value):
        self._data = value
 
    @x.deleter
    def x(self):
        del self._data
 
    def __init__(self, val):
        self._data = val
        self.x = 3
        self.name = ‘test’

接下来调用

1
2
3
4
s = MyClass(99)
print s.x
print s.name
print s.__dict__

很明显x是资料描述器,而name不是,所以结果是

3
5
{‘_data’: 3, ‘name’: ‘test’}

如果给classproperty加上__set__,那么就会调用被装饰的name,而不是实例化时实例字典中的name

一个property的python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Property(object):
    “Emulate PyProperty_Type() in Objects/descrobject.c”
 
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.__doc__ = doc
 
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError, “unreadable attribute”
        return self.fget(obj)
 
    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError, “can’t set attribute”
        self.fset(obj, value)
 
    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError, “can’t delete attribute”
        self.fdel(obj)
 
    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)
 
    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)
 
    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

1
2 收藏

评论





转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn

8 Comments