您当前的位置:首页 > IT编程 > python
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:Python 函数装饰器应用教程

51自学网 2022-02-21 10:46:37
  python
这篇教程Python 函数装饰器应用教程写得很实用,希望能帮到您。

一、什么是函数装饰器

1.函数装饰器是Python提供的一种增强函数功能的标记函数;

2.装饰器是可调用的函数对象,其参数是另一个函数(被装饰的函数);

我们可以使用修饰器来封装某个函数,从而让程序在执行这个函数之前与执行完这个函数之后,分别运行某些代码。这意味着,调用者传给函数的参数值、函数返回给调用者的值,以及函数抛出的异常,都可以由修饰器访问并修改。这是个很有用的机制,能够确保用户以正确的方式使用函数,也能够用来调试程序或实现函数注册功能,此外还有许多用途。

二、函数装饰器的执行时机

函数装饰器在被装饰函数编译解析之后直接执行,装饰器是按照从上到下执行的;

函数装饰器内部定义的返回函数依附在装饰器的执行环境中;

函数装饰器每次执行都会生成新的返回函数;

import sysdef 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;@dec1def m1():    print(f'{sys._getframe().f_code.co_name}')@dec1def 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=6def f():    print(b)f()# 6
b=6def f():    print(b)    b = 9f()# UnboundLocalError: local variable 'b' referenced before assignment

通过生成的字节码可以看到两者对变量b的处理的差异,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是给b赋值却在print之后导致报错;

from dis import disb=6def f():    print(b)    # b = 9dis(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 disb=6def 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=6def f():    global b    print(b)    b = 9f()# 6

四、闭包

闭包是是指可以访问非在函数体内定义的非全局变量的函数;

通过函数的__code__及__closure__可以看到局部变量和自由变量及闭包的情况;

def makesum():    sum = [0]    def s(val):        sum[0] += val        return sum[0]    return ss = 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 ss = 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 functoolsdef dec(func):    def real():        '''this is real function'''        pass    return realdef wrapsdec(func):    @functools.wraps(func)    def real():        '''this is real function'''        pass    return real@decdef max(nums):    '''this is max function'''    pass@wrapsdecdef 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@decdef sum(*nums, **kwargs):    s = 0    for n in nums:        s = s + n    for a in kwargs.values():        s = s + a    return sprint(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 mathimport numbersfrom functools import singledispatch@singledispatchdef 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会将被装饰的函数对象作为第一个参数传递给装饰器;

我们可以通过在函数装饰器外再嵌套一个函数工厂来承载装饰特定函数的时候设置的参数;
Python实现微信好友数据爬取及分析
Python爬虫实战之爬取某宝男装信息

万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。