Python 函数装饰器应用教程
作者:无风听海 发布时间:2022-08-17 05:53:24
一、什么是函数装饰器
1.函数装饰器是Python提供的一种增强函数功能的标记函数;
2.装饰器是可调用的函数对象,其参数是另一个函数(被装饰的函数);
我们可以使用修饰器来封装某个函数,从而让程序在执行这个函数之前与执行完这个函数之后,分别运行某些代码。这意味着,调用者传给函数的参数值、函数返回给调用者的值,以及函数抛出的异常,都可以由修饰器访问并修改。这是个很有用的机制,能够确保用户以正确的方式使用函数,也能够用来调试程序或实现函数注册功能,此外还有许多用途。
二、函数装饰器的执行时机
函数装饰器在被装饰函数编译解析之后直接执行,装饰器是按照从上到下执行的;
函数装饰器内部定义的返回函数依附在装饰器的执行环境中;
函数装饰器每次执行都会生成新的返回函数;
import sys
def dec1(m):
print(f'{sys._getframe().f_code.co_name} is execute, arg {m.__name__}')
def newm1():
print(f'{sys._getframe().f_code.co_name}')
return newm1;
@dec1
def m1():
print(f'{sys._getframe().f_code.co_name}')
@dec1
def m11():
print(f'{sys._getframe().f_code.co_name}')
if __name__ == '__main__':
print(m1)
print(m11)
print(f'm1==m11:{m1==m11}')
# dec1 is execute, arg m1
# dec1 is execute, arg m11
# <function dec1.<locals>.newm1 at 0x7fdfa97d9160>
# <function dec1.<locals>.newm1 at 0x7fdfa97d91f0>
# m1==m11:False
三、变量作用域
Python中将变量声明和赋值操作合一,很容易导致函数局部变量覆盖函数外的变量
b=6
def f():
print(b)
f()
# 6
b=6
def f():
print(b)
b = 9
f()
# UnboundLocalError: local variable 'b' referenced before assignment
通过生成的字节码可以看到两者对变量b的处理的差异,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是给b赋值却在print之后导致报错;
from dis import dis
b=6
def f():
print(b)
# b = 9
dis(f)
# 5 0 LOAD_GLOBAL 0 (print)
# 2 LOAD_GLOBAL 1 (b)
# 4 CALL_FUNCTION 1
# 6 POP_TOP
# 8 LOAD_CONST 0 (None)
# 10 RETURN_VALUE
from dis import dis
b=6
def f():
print(b)
b = 9
# 5 0 LOAD_GLOBAL 0 (print)
# 2 LOAD_FAST 0 (b)
# 4 CALL_FUNCTION 1
# 6 POP_TOP
# 6 8 LOAD_CONST 1 (9)
# 10 STORE_FAST 0 (b)
# 12 LOAD_CONST 0 (None)
# 14 RETURN_VALUE
可以使用global来强制声明b是全局变量,然后就可以重新赋值了;
b=6
def f():
global b
print(b)
b = 9
f()
# 6
四、闭包
闭包是是指可以访问非在函数体内定义的非全局变量的函数;
通过函数的__code__及__closure__可以看到局部变量和自由变量及闭包的情况;
def makesum():
sum = [0]
def s(val):
sum[0] += val
return sum[0]
return s
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f63321f1b20: list object at 0x7f63321e8a00>,)
# [3]
基于三中Python变量作用域的缘故,上边的sum只能使用列表对象,python提供的nonlocal关键字可以直接使用int类型的变量;
def makesum():
sum = 0
def s(val):
nonlocal sum
sum += val
return sum
return s
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f73e6a4ab20: int object at 0x7f73e6b47970>,)
# 3
五、保留函数的元数据
函数装饰器默认会使用返回的函数完全取代被装饰的函数,这样可能会导致序列化或者开发工具智能提示的问题;可以使用functools.wraps来保留原始函数的标准属性(name、module、__annotations__等);
import functools
def dec(func):
def real():
'''this is real function'''
pass
return real
def wrapsdec(func):
@functools.wraps(func)
def real():
'''this is real function'''
pass
return real
@dec
def max(nums):
'''this is max function'''
pass
@wrapsdec
def sum(nums):
'''this is sum function'''
print(max)
print(max.__name__)
print(max.__doc__)
print(help(max))
print()
print(sum)
print(sum.__name__)
print(sum.__doc__)
print(help(sum))
# <function dec.<locals>.real at 0x7f1bfd4241f0>
# real
# this is real function
# Help on function real in module __main__:
#
# real()
# this is real function
#
# None
#
# <function sum at 0x7f1bfd424280>
# sum
# this is sum function
# Help on function sum in module __main__:
#
# sum(nums)
# this is sum function
#
# None
六、支持关键字参数、位置参数
def dec(func):
def real(*args, **kwargs):
result = func(*args, **kwargs)
return result
return real
@dec
def sum(*nums, **kwargs):
s = 0
for n in nums:
s = s + n
for a in kwargs.values():
s = s + a
return s
print(sum(1,2,3,first=1))
七、使用lru_cache缓存函数执行结果
lru_cache内部使用函数的参数作为key,使用dict进行缓存执行结果,减少重复计算;
默认支持缓存128条记录,超过后采用LRU方式丢弃多余记录;
需要注意执行中不要改变参数,否则会影响字典key的有效性;
from functools import lru_cache
@lru_cache()
def fibonacci(n):
print(f'fibonacci({n})')
if n<2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(6))
# fibonacci(6)
# fibonacci(5)
# fibonacci(4)
# fibonacci(3)
# fibonacci(2)
# fibonacci(1)
# fibonacci(0)
# 8
八、使用singledispatch实现泛型函数
python不支持方法或者函数的重载,我们无法单独定义不同参数类型的同名函数;
singledispatch提供了这样一种能力,其通过注册具体的参数类型和关联的函数;
我们可以在自己的模块定义自己的类型,并实现自己的自定义函数;
import math
import numbers
from functools import singledispatch
@singledispatch
def absall(obj):
return abs(obj)
@absall.register(numbers.Number)
def numabs(num):
return abs(num)
@absall.register(numbers.Complex)
def cabs(c):
return math.sqrt(c.real*c.real + c.imag* c.imag)
class Line:
def __init__(self, startx, starty, endx, endy):
self.startx = startx
self.starty = starty
self.endx = endx
self.endy = endy
@absall.register(Line)
def lineabs(line):
y =line.endy - line.starty
x = line.endx - line.startx
return math.sqrt(x*x + y*y)
print(absall(-1.1))
print(absall(3+4j))
l = Line(1,2,4,6)
print(absall(l))
# 1.1
# 5.0
# 5.0
九、通过参数控制函数装饰器的行为
函数装饰器本身不支持传递参数,解析源代码的时候,python会将被装饰的函数对象作为第一个参数传递给装饰器;
我们可以通过在函数装饰器外再嵌套一个函数工厂来承载装饰特定函数的时候设置的参数;
def accesscontrol(checkuser=True, updatesession=True):
def dec(func):
def checkuserf():
print('check user')
return True
def updatesessionf():
print('update session')
return True
def access():
if checkuser:
checkuserf()
if updatesession:
updatesessionf()
func()
return access
return dec
@accesscontrol()
def pushitem():
print('pushitem')
return True
@accesscontrol(checkuser=False)
def getitem():
print('getitem')
return True
# pushitem()
# print()
# getitem()
#
# check user
# update session
# pushitem
#
# update session
# getitem
来源:https://www.cnblogs.com/wufengtinghai/p/15631730.html


猜你喜欢
- 阅读上一篇:请给PNG8一个机会 系列二:对png8的误解Png8的在ie中的怪异表现:1.半透明的png8在ie6以下的浏览器显示为全透明
- 1.两种查询引擎查询速度(myIsam 引擎 )InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from
- 0x00 识别涉及技术验证码识别涉及很多方面的内容。入手难度大,但是入手后,可拓展性又非常广泛,可玩性极强,成就感也很足。验证码图像处理验证
- 视频本教程的视频碰撞是怎么回事在上一次教程中,我们添加了图形,将精灵从普通矩形更改为更漂亮的PNG图像。然而,这带来了一个问题:有时游戏会认
- 使用opencv相关函数确定图片中的直线#pip install opencv-python==4.4.0.42 opencv-contri
- 浏览器中某些计算和处理要比其他的昂贵的多。例如,DOM操作比起非DOM交互需要更多的内存和CPU时间。连续尝试进行过多的DOM相关操作可能会
- mysql中文排序方法1 mysql在查询字符串时是大小写不敏感的,在编绎mysql时一般以ISO-8859字符集作为默认的字符集,因此在比
- SQL Server Sa用户相信大家都有一定的理解,下面就为您介绍SQL Server 2000身份验证模式的修改方法及SQL Serve
- 写好脚本,注册好服务之后,经测试,ORACLE可以随RHEL启动而启动,但不能随系统关闭而关闭。在网上找答案,发现几乎所有的设置过程帖子都是
- 首先来说,让一个游戏赋有可玩性必须要动静结合。(哈哈,大家以为我要讲作文了。。。但其实我今天要讲的是Javascript)静态的东西谁不会做
- 如果出现 automation服务器不能创建对象 解决方法:1、如果是Scripting.FileSystemObje
- 本文实例讲述了Python wxPython库使用wx.ListBox创建列表框。分享给大家供大家参考,具体如下:如何创建一个列表框?列表框
- 为index.php文件设置只读属性后,木马就没权限给你文件末尾追加广告了。下面我们看具体的代码,设置index.php只读:<?ph
- 本文实例代码主要实现python编程测试电脑开启最大线程数,具体实现代码如下。#!/usr/bin/env python #co
- 一、说明前面我们说了mysql的安装配置,mysql语句使用以及备份恢复mysql数据;本次要介绍的是mysql的主从复制,读写分离;及高可
- 如果你听说过“测试驱动开发”(TDD:Test-Driven Development),单元测试就不陌生。单元测试是用来对一个模块、一个函数
- 一、作用创建一个新的Tensor,该Tensor的type和device都和原有Tensor一致,且无内容。二、使用方法如果随机定义一个大小
- 关于django models中添加字段的一个小节,记录下django的models中已经写好了字段,可是后面我又想在添加一些字段,于是就在
- 前面提到了银行转账这个场景,展示了一个比较耗时的转账操作。这篇继续转帐,下面展示一段程序,多个线程的操作都更改了amount变量导致运行结果
- 最近想用原生JS多实现一些小功能,现在写到博客里,大家可以借鉴,有问题欢迎指出。轮播图需求:图片循环轮播,可点击左右切换,切换状态与<