美文网首页
python语法 -- 描述符与装饰器

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

作者: 坏科学 | 来源:发表于2018-10-21 21:22 被阅读0次
    描述符

    描述符是一种属性托管的方法,一个类只要实现了__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: <class '__main__.TestDes'>
    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
    >>> <class '__main__.TestDes'> (1,) {}
    >>> ori_class_method func invoked
    >>> <class '__main__.TestDes'> (1,) {}
    >>> ori_class_method func invoked
    >>> <class '__main__.TestDes'> (1,) {}
    >>> my_class_method func invoked
    >>> <class '__main__.TestDes'> (1,) {}
    >>> my_class_method func invoked
    >>> myx
    

    相关文章

      网友评论

          本文标题:python语法 -- 描述符与装饰器

          本文链接:https://www.haomeiwen.com/subject/cylyvftx.html