python grpc unary call错误_python的黑魔法-装饰器

7128b43823742be8df82a54f16cda009.png

python的装饰器

装饰器 简单的来说就是函数的函数,在执行目标函数时,先执行装饰器函数,很像HOOk

简单的装饰器

实现一个打印日志的装饰器

def myLogger(func):
    def wrapper(*args, **kw):
        print('开始执行:{} 函数了:'.format(func.__name__))
        result = func(*args, **kw)
        print('{}执行完啦。'.format(func.__name__))
        return result
    return wrapper

myLogger是装饰器, 参数func是目标函数,返回值也是个函数,所以说装饰器是函数的函数

@myLogger
def add(a,b):
    return a + b
print(add(1,2))
开始执行:add 函数了:
add执行完啦。
3
@myLogger
def sub(a,b):
    return a - b

sub(2,1)

开始执行:sub 函数了:
sub执行完啦。
1

装饰器很灵活,只要加个装饰器,就实现了每个函数的打印日志的功能

带参数的装饰器

实现一个函数运行失败时,自动重新运行的装饰器,这在爬虫中经常看到。我们传给装饰器自动尝试运行的次数的参数

def myTry(try_nums):
    def myfunc(func):
        def wrapper(*args, **kw):
            i = 0
            while i < try_nums:
                print('开始发起{}次请求'.format(i+1))
                try:
                    result = func(*args, **kw)
                    return result
                except Exception as e:# 这个地方要专门捕获 超时,连接中断等错误
                    error = e
                    i += 1
            return error
        return wrapper
    return myfunc

这里不使用爬虫程序,使用其他函数看看效果。

@myTry(3)
def add(a,b):
    return a+b


add(1,2)
开始发起1次请求
3


add('1',2)
开始发起1次请求
开始发起2次请求
开始发起3次请求
TypeError('must be str, not int')

不带参数的类装饰器

上面都是基于函数实现的装饰器,其实类也能当作装饰器

基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。 init :接收被装饰函数, call :实现装饰逻辑。

实现一个收集错误信息的类装饰器

class MyExcept():
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('{} 函数开始执行'.format(self.func.__name__))
        data = {'status':1, 'msg':'', 'result': ''}
        try:
            result = self.func(*args, **kwargs)
            data['result'] = result
        except Exception as e:
            data['status'], data['msg'] = 0, e
        return data
@MyExcept
def add(a,b):
    return a+b

add(1,1)

add 函数开始执行
{'status': 1, 'msg': '', 'result': 2}


add('1',1)

add 函数开始执行
{'status': 0, 'msg': TypeError('must be str, not int'), 'result': ''}

带参数的类装饰器

带参数和不带参数的类装饰器还是有一定的差别

实现一个自动尝试运行和收集错误信息的类装饰器

class MyExcept():
    def __init__(self, try_nums):
        self.try_nums = try_nums

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            i = 0
            while i < self.try_nums:
                print('{} 函数开始第{}次执行'.format(func.__name__,i+1)) 
                try:
                    result = func(*args, **kwargs)
                    data = {'status':1, 'msg':'', 'result': result}
                    break
                # except (timeout): #要捕获的错误允许重新尝试
                # i += 1
                # data = {'status':0, 'msg':'超时', 'result': ''}
                except Exception as e: # #其他错误不允许重新尝试
                    data = {'status':0, 'msg':e, 'result': ''}
                    i += 1
            return data
        return wrapper

@MyExcept(3)
def add(a,b):
    return a+b

add(1,1)
add 函数开始第1次执行
{'status': 1, 'msg': '', 'result': 2}


add('a',1)

add 函数开始第1次执行
add 函数开始第2次执行
add 函数开始第3次执行
{'status': 0, 'msg': TypeError('must be str, not int'), 'result': ''}

通过上面几个装饰器的实现,是否已经明白装饰器的本质了。

对于带参数的装饰器,它是在不带参数的装饰器的基础上,在外面又封装了一层函数,用于接收参数,然后在装饰器内部使用这些参数,相当于带参数的装饰器执行完之后返回了一个装饰器。

对于装饰器,本质就是要把目标函数当作参数传入装饰器。 事实上,Python 对某个对象是否能通过装饰器( @decorator)形式使用只有一个要求:decorator 必须是一个“可被调用(callable)的对象。 所以类要实现call函数

add
<function __main__.MyExcept.__call__.<locals>.wrapper(*args, **kwargs)>

add.__name__
'wrapper'

add 经过装饰器之后,实际上内部已经经过某些处理,使得add函数变成了wrapper函数,所以表面上看我们在调用了add函数,实际上调用的wrapper函数。

经过哪些处理实际上也很好理解,还是上面的例子,添加一些提示。

from functools import wraps

class MyExcept():
    def __init__(self, try_nums):
        print('类开始初始化')
        self.try_nums = try_nums

    def __call__(self, func):
        print('开始调用函数')
        @wraps(func)
        def wrapper(*args, **kwargs):
            i = 0
            while i < self.try_nums:
                print('{} 函数开始第{}次执行'.format(func.__name__,i+1)) 
                try:
                    result = func(*args, **kwargs)
                    data = {'status':1, 'msg':'', 'result': result}
                    break
                # except (timeout): #要捕获的错误允许重新尝试
                # i += 1
                # data = {'status':0, 'msg':'超时', 'result': ''}
                except Exception as e: # #其他错误不允许重新尝试
                    data = {'status':0, 'msg':e, 'result': ''}
                    i += 1
            return data
        print('返回函数wrapper')
        return wrapper

@MyExcept(3)
def add(a,b):
    return a+b

类开始初始化
开始调用函数
返回函数wrapper

在我们还没有调用函数时,函数已经开始调用,并返回了一个函数,实际上add 已经指向了内部wrapper函数了

第一步,装饰器执行装饰器的参数,返回一个callable类型的对象,而且这个对象的参数必须是一个函数。
第二步,返回的对象把目标函数当作参数,执行后,返回一个函数赋值给目标函数(目标函数指向返回函数)。
第三步,等待正常调用。

add
<function __main__.add(a, b)>

add.__name__
'add'

这里使用 functools .wraps 装饰器,它的作用就是将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉。

既然装饰器可以装饰函数,那装饰器可以不可以装饰类呢,也是可以的。

装饰类的装饰器

实现单例模式的装饰器

def SingleInstance(cls):
    def new(cls,*args,**kwargs):
        if not  hasattr(cls,'instance'):
            cls.instance= object.__new__(cls)
        return cls.instance
    cls.__new__=new
    return cls

@SingleInstance
class User:
    _instance = None

    def __init__(self, name):
        self.name = name

u1 = User('jack')

u1.name
'jack'

u2 = User('tom')
u2.age = 20

u2.name
'tom'

print(u1.name, u1.age)
tom 20

u1 is u2
True
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值