python语法 -- 描述符与装饰器

描述符

描述符是一种属性托管的方法,一个类只要实现了__get__、__set__、__del__(未必全部实现),并且该类的实例是另一个类的类属性,那么该类就称为描述符。

描述符可以方便的对属性进行类型检查、保护属性不被修改、为属性增加依赖等。

一个简单的描述符的例子:

class a_des(object):

    def __init__(self):
        self.__value = 'a_des'

    def __get__(self, instance, owner):
        print('>>>> get: %s owner: %s' % (self.__value, owner))
        return self.__value
    
    def __set__(self, instance, value):
        print('>>>> set')
        if not isinstance(value, str):
            raise 'invalid value type'
        self.__value = 'i am data des ' + value
class TestDes(object):
    a = a_des()

    def __init__(self, value):
        self.a = value

t = TestDes('instance a')
>>>> set
>>>> get: i am data des instance a owner: 
i am data des instance a

注意,实例属性的访问顺序:数据描述符 > 实例属性 > 非数据描述符 > __getattri__


装饰器

装饰器(decorator)是一个返回函数的高阶函数,是python中一种在代码运行期间动态增加功能的方式。

举个 fibonacci

def fibonacci(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2) 

分别打印函数运用前后的传参和返回值

def log(func):
    def wrapper(*args, **kw):
        print('func parameter %r - %r', args, kw)
        result = func(*args, **kw)
        print('func result %r', result)
        return result
    return wrapper

调用 fibonacci 函数时只需如下修饰即可

@log
def fibonacci(n):
    ....
    pass

>> fibonacci(3)
func parameter %r - %r (3,) {}
func parameter %r - %r (2,) {}
func result %r 1
func parameter %r - %r (1,) {}
func result %r 1
func result %r 2

把 @log 放在 fibonacci 函数的定义处,相等于执行

fibonacci = log(fibonacci)

由于log()是一个decorator,返回一个函数,所以,原来的fibonacci()函数仍然存在,只是现在同名的fibonacci变量指向了新的函数,于是调用fibonacci()将执行新函数,即在log()函数中返回的wrapper()函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('decorator text %s', text)
            print('func parameter %r - %r', args, kw)
            result = func(*args, **kw)
            print('func result %r', result)
            return result
        return wrapper
    return decorator

@log('test_decorator')
def fibonacci(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2) 

note, 由于函数也是对象,有__name__等属性。将函数修饰后,函数变成wrapper,所以:

>> fibonacci.__name__
wrapper

这在一些可能使用函数属性的地方造成错误,好在python内置模块functools.wraps可以消除装饰器的影响,使用如下

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('func parameter %r - %r', args, kw)
        result = func(*args, **kw)
        print('func result %r', result)
        return result
    return wrapper

>> fibonacci.__name__
fibonacci

自定义实现装饰器方法
staticmethod

class DDDStaticMethod(object):

    def __init__(self, func):
        self.func = func
        super(DDDStaticMethod, self).__init__() 

    def __get__(self, isinstance, owner):
        return self.func

def dddstaticmethod(func):
    return DDDStaticMethod(func)

classmethod

class DDDClassMethod(object):

    def __init__(self, func):
        self.func = func
        super(DDDClassMethod, self).__init__()

    def __get__(self, isinstance, owner):
        def method(*args, **kwargs):
            return self.func(owner, *args, **kwargs)
        return method

def dddclassmethod(func):
    return DDDClassMethod(func)

property

class DDDPropertyMethod(object):

    def __init__(self, fset=None, fget=None, fdel=None):
        self.fset = fset
        self.fget = fget
        self.fdel = fdel
        super(DDDPropertyMethod, self).__init__()

    def __get__(self, instance, owner):
        if not self.fget:
            raise AttributeError('')
        res = self.fget(instance)
        return res

    def __set__(self, instance, value):
        if not self.fset:
            raise AttributeError('')
        self.fset(instance, value)

    def setter(self, fset):
        return type(self)(fset=fset, fget=self.fget)

    def getter(self, fget):
        return type(self)(fset=self.fset, fget=fget)

dddpropertymethod = DDDPropertyMethod()

测试

class TestDes(object):

    def __init__(self, value):
        self._pvalue = None
        super(TestDes, self).__init__()

    @staticmethod
    def ori_static_method(*args, **kwargs):
        print('>>> %s %s' % (args, kwargs))
        print('>>> ori_static_method func invoked')

    @classmethod
    def ori_class_method(cls, *args, **kwargs):
        print('>>> %s %s %s' % (cls, args, kwargs))
        print('>>> ori_class_method func invoked')

    @dddstaticmethod
    def my_static_method(*args, **kwargs):
        print('>>> %s %s' % (args, kwargs))
        print('>>> my_static_method func invoked')

    @dddclassmethod
    def my_class_method(cls, *args, **kwargs):
        print('>>> %s %s %s' % (cls, args, kwargs))
        print('>>> my_class_method func invoked')

    @dddpropertymethod.getter
    def my_x(self):
        return self._pvalue

    @my_x.setter
    def my_x(self, value):
        self._pvalue = value

t = TestDes('instance a')
TestDes.ori_static_method(1)
t.ori_static_method(1)
TestDes.my_static_method(1)
t.my_static_method(1)
TestDes.ori_class_method(1)
t.ori_class_method(1)
TestDes.my_class_method(1)
t.my_class_method(1)
t.my_x = 'myx'
print(t.my_x)

>>> 输出
>>> (1,) {}
>>> ori_static_method func invoked
>>> (1,) {}
>>> ori_static_method func invoked
>>> (1,) {}
>>> my_static_method func invoked
>>> (1,) {}
>>> my_static_method func invoked
>>>  (1,) {}
>>> ori_class_method func invoked
>>>  (1,) {}
>>> ori_class_method func invoked
>>>  (1,) {}
>>> my_class_method func invoked
>>>  (1,) {}
>>> my_class_method func invoked
>>> myx

你可能感兴趣的:(python语法 -- 描述符与装饰器)