您的位置:首页 > 编程语言 > Python开发

Python学习笔记(12)-装饰器详解

2017-01-16 23:57 627 查看
之前在笔记(11)中,简单写了装饰器的用法,看了廖老师的教程感觉头大了不少,老纠结
return
到底要不要,又网上搜集许多讲解,现分步骤详细描述下一个常规的装饰器的写法。不足之处今后再补充。(日志的打印格式我是手动改的,为了看着舒服额已→_→)

1.最简单的输出时间的函数

代码:(就是简单的输出字符串)

def myfun():
print('├2017-01-16┤')

myfun()


结果:

├2017-01-16┤


2.使用装饰器,在输出的前后各加上log日志

代码:(简单的使用装饰器)

def log(fun):
print('┌--before--┐')
fun()  # 此处调用传进来的函数
print('└--after---┘')

def myfun():
print('├2017-01-16┤')

myfun = log(myfun)  # 将函数传递给log函数,再用变量myfun指向它
print(myfun)  # 此时的myfun没指向任何内存地址,因为log函数没有返回值


结果:

┌--before--┐
├2017-01-16┤└--after---┘
None


3.利用Python的@语句

代码:(使用
@
语法,吧
decorator
放在函数的定义的地方)

def log(fun):
print('┌--before--┐')
fun()
print('└--after---┘')

@log  # 只在此处添加了@log 此句相当于 myfun = log(myfun)
def myfun():
print('├2017-01-16┤')

# myfun = log(myfun)
print(myfun)  # 仍旧没有指向内存地址


结果:

┌--before--┐
├2017-01-16┤└--after---┘
None


4.以上的写法只能调用一次,若要多次调用,则需要在装饰器内定义包装函数

代码:(log内定义包装函数,并将此函数返回)

def log(fun):
def cover():  # 定义包装函数
print('┌--before--┐')
fun()
print('└--after---┘')

return cover  # 将包装函数返回,注意返回的是函数名(即变量),不是调用函数cover()

@log  # 此时 myfun = log(myfun) 中,myfun指向了cover,但是并没有调用,和第3步直接调用不同
def myfun():
print('├2017-01-16┤')

print(myfun)  # myfun已经指向了cover
myfun()
myfun()  # 可调用多次


结果:log处并没有调用,所以一共输出2次

<function log.<locals>.cover at 0x0000000000BCDD08>
┌--before--┐
├2017-01-16┤└--after---┘
┌--before--┐
├2017-01-16┤└--after---┘


5.自定义函数中带有参数和返回值

代码:(只是将包装函数增加参数)

def log(fun):
def cover(a, b):  # 此处的参数需要和原函数参数相同
print('┌--before--┐')
result = fun(a, b) # 调用函数后,将结果赋值给result变量
print('└--after---┘')
return result  # 此时可返回结果

return cover

@log
def myfun(a, b):
print('├2017-01-16┤')
return a * b

print(myfun)
print(myfun(3, 6)) # 函数结果已经返回
print(myfun(6, 9))


结果:

<function log.<locals>.cover at 0x000000000073DD08>
┌--before--┐
├2017-01-16┤└--after---┘
18
┌--before--┐
├2017-01-16┤└--after---┘
54


因为自定义的函数参数类型和个数不确定,所以根据之前函数参数的笔记(7),可以将任意函数参数表示成
func(*args, **kw)
,所以上述代码
cover
函数的定义可以改为

def cover(*args, **kwargs):  # 此处的参数需要和原函数参数相同
print('┌--before--┐')
result = fun(*args, **kwargs)
print('└--after---┘')
return result


此时不论其他函数多少个参数,都会按照装饰器的逻辑在函数执行前后加上log语句

6.装饰器携带参数的情况

代码:(比如,自定义log文本内容(装饰器外控制))

def log(message):  # log传入的参数变为14行传入的参数
def deco(fun):  # 参数fun是原函数
def cover(*args, **kwargs):
print('┌--%s--┐' % message)  # 使用传入的参数message打印日志
result = fun(*args, **kwargs)
print('└--%s--┘' % message)
return result

return cover

return deco

@log('my message')  # 此句相当于 myfun = log('my message')(myfun)
def myfun(a, b):
print('├--2017-01-16--┤')
return a * b

print(myfun)
print(myfun(3, 6))
print(myfun(6, 9))


结果:

<function log.<locals>.deco.<locals>.cover at 0x00000000011CDD90>
┌--my message--┐
├--2017-01-16--┤
└--my message--┘
18
┌--my message--┐
├--2017-01-16--┤
└--my message--┘
54


e.g. 同时兼容
log
log('sth')


代码:

def log(message):  # log传入的参数变为14行传入的参数
def deco(fun, msg=message):  # 参数fun是原函数
def cover(*args, **kwargs):
print('┌--%s--┐' % msg)  # 使用传入的参数打印日志
result = fun(*args, **kwargs)
print('└--%s--┘' % msg)
return result

return cover

if isinstance(message, str): # 是字符串的情况下,正常执行
return deco
else:
return deco(message, '---') # 不是字符串或者为空等的情况,此时的message是传过来的函数,调用deco(),返回变量指向cover函数

@log('my message')  # 此句相当于 myfun = log('my message')(myfun)
def myfun(a, b):
print('├--2017-01-16--┤')
return a * b

@log
def myfun2():
print('├-other-┤')

myfun(1, 2)
myfun2()


结果:

┌--my message--┐
├--2017-01-16--┤
└--my message--┘
┌-------┐
├-other-┤
└-------┘
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python