PYTHON里的装饰器能装饰类吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PYTHON里的装饰器能装饰类吗相关的知识,希望对你有一定的参考价值。

可以的啦

# -*- coding: UTF-8 -*-
from functools import wraps

__author__ = 'lpe234'


def singleton(cls):
    """
    装饰器 实现单例模式
    :param cls:
    :return:
    """
    instances = 

    @wraps(cls)
    def _singleton(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return _singleton


@singleton
class SelfClass(object):
    pass


def main():
    s1 = SelfClass()
    s2 = SelfClass()
    assert s1 is s2

if __name__ == '__main__':
    main()

参考技术A

Python 装饰器装饰类中的方法

目前在中文网上能搜索到的绝大部分关于装饰器的教程,都在讲如何装饰一个普通的函数。本文介绍如何使用Python的装饰器装饰一个类的方法,同时在装饰器函数中调用类里面的其他方法。本文以捕获一个方法的异常为例来进行说明。

有一个类Test, 它的结构如下:

class Test(object):
def __init__(self):
pass

def revive(self):
print('revive from exception.')
# do something to restore

def read_value(self):
print('here I will do something.')
# do something.

在类中有一个方法read_value(),这个方法在多个地方被调用。由于某些原因,方法read_value有可能随机抛出Exception导致程序崩溃。所以需要对整个方法做try ... except处理。最丑陋的做法如下面的代码所示:

class Test(object):
def __init__(self):
pass

def revive(self):
print('revive from exception.')
# do something to restore

def read_value(self):
try:
print('here I will do something.')
# do something.
except Exception as e:
print(f'exception e raised, parse exception.')
# do other thing.
self.revive()

这样写虽然可以解决问题,但是代码不Pythonic。

使用装饰器来解决这个问题,装饰器函数应该写在类里面还是类外面呢?答案是,写在类外面。那么既然写在类外面,如何调用这个类的其他方法呢?

首先写出一个最常见的处理异常的装饰器:

def catch_exception(origin_func):
def wrapper(*args, **kwargs):
try:
u = origin_func(*args, **kwargs)
return u
except Exception:
return 'an Exception raised.'
return wrapper


class Test(object):
def __init__(self):
pass

def revive(self):
print('revive from exception.')
# do something to restore

@catch_exception
def read_value(self):
print('here I will do something.')
# do something.

这种写法,确实可以捕获到origin_func()的异常,但是如果在发生异常的时候,需要调用类里面的另一个方法来处理异常,这又应该怎么办?答案是给wrapper增加一个参数:self.

代码变为如下形式:

def catch_exception(origin_func):
def wrapper(self, *args, **kwargs):
try:
u = origin_func(self, *args, **kwargs)
return u
except Exception:
self.revive() #不用顾虑,直接调用原来的类的方法
return 'an Exception raised.'
return wrapper


class Test(object):
def __init__(self):
pass

def revive(self):
print('revive from exception.')
# do something to restore

@catch_exception
def read_value(self):
print('here I will do something.')
# do something.

只需要修改装饰器定义的部分,使用装饰器的地方完全不需要做修改。

下图为正常运行时的运行结果:

下图为发生异常以后捕获并处理异常:

通过添加一个self参数,类外面的装饰器就可以直接使用类里面的各种方法,也可以直接使用类的属性。

python装饰器系列

带参数的装饰器的应用:

比如有一个函数,只有在对有许可权限的用户开放,执行此函数的用户没有在认证列表里的,就不会执行这个函数。这个该如何实现呢?如下:

 1 def check(allow_users):
 2     def inner_check(fn):
 3         def wrap(username,*args,**kwargs):
 4             ‘‘‘This is wrap‘‘‘
 5             if username in allow_users:
 6                 return fn(username,*args,**kwargs)
 7             return "You are illegal users"
 8         return wrap
 9     return inner_check
10  
11 
12 @check([zhaochj,zcj])
13 def private(username):
14     ‘‘‘The authentication‘‘‘
15     return "You are legitimate users"
1 private(zhaochj)
2 You are legitimate users
1 private(tom)
2 You are illegal users

这样就可以对调用函数进行身份验证

python中一个函数有一些属性是函数本身具有的,比如__name__属性是查看函数名称,如果一个函数被装饰器装饰过后,这个函数的这些属性会发生怎样的变化呢?

以上边的check装饰器和private函数为例子,如下:

1 private.__name__
2 wrap

private函数的名称是wrap了,这是怎么回事?上边已经说过,这里的private函数被装饰器装饰后它已不再是原来的private函数,private这个函数名称会被指向到wrap这个函数对象,那当然用上边的private.__name__查看函数的属性就会是wrap函数的属性。

那怎样来修正呢?可以这样做,如下:

 

 1 def check_1(allow_users):
 2     def inner_check(fn):
 3         def wrap(username,*args,**kwargs):
 4             ‘‘‘This is wrap‘‘‘
 5             if username in allow_users:
 6                 return fn(username,*args,**kwargs)
 7             return "You are illegal users"
 8         wrap.__name__ = fn.__name__
10         return wrap
11     return inner_check
12  
13 @check_1([zhaochj,zcj])
14 def private_1(username):
15     ‘‘‘The authentication‘‘‘
16     return "You are legitimate users"

 

1 private_1.__name__
2 private_1

通过在装饰器把__name__重新赋值后就能更正这个问题,但对一个函数来说像__name__这样类似的属性有许多,如果都是这样手工来修正显然是不现实的,所以python提供了一个wraps装饰器来自动修正这个问题,wrapsfunctools这个包中,所以可以这样来修正这个问题,如下:

 1 import functools
 2  
 3 def check_1(allow_users):
 4     def inner_check(fn):
 5         @functools.wraps(fn)
 6         def wrap(username,*args,**kwargs):
 7             ‘‘‘This is wrap‘‘‘
 8             if username in allow_users:
 9                 return fn(username,*args,**kwargs)
10             return "You are illegal users"
11         return wrap
12     return inner_check
13  
14 @check_1([zhaochj,zcj])
15 def private_1(username):
16     ‘‘‘The authentication‘‘‘
17     return "You are legitimate users"
1 private_1.__name__
2 private_1

@functools.wraps(fn)这个装饰器相当于执行了wrap.__name__ = fn.__name__这样的操作

 

以上是关于PYTHON里的装饰器能装饰类吗的主要内容,如果未能解决你的问题,请参考以下文章

装饰器详解

Python装饰器的写法以及应用场景

IntelliJ 可以自动创建装饰器类吗?

装饰器的写法以及应用环境

Python的黑魔法@property装饰器的使用技巧

ES6装饰器Decorator基本用法