Python(6)高阶函数之装饰器和偏函数

简介: Python(6)高阶函数之装饰器和偏函数

一、装饰器


  • 由于函数也是一个对象,并且函数可以赋值给变量,赋值后的变量可以直接调用函数:


>>> def test():
...     print("aaaaaa1") 
... 
>>> f = test
>>> f
<function test at 0x0000022862EBE7A0>
>>> f()
使用__name__可以获取函数的函数名:
>>> test.__name__
'test'
>>> f.__name__
'test'

如果要增加test( )函数的功能,例如在函数调用前后增加输出内容,想要达到这种效果,我们可以直接修改函数,但是如果说不允许修改源函数,又需要增加函数功能该怎么做呢,这种情况我们可以使用装饰器(Decorator)


装饰器其实就是在不修改源函数的情况下,在代码运行期间动态增加函数功能的方式,而装饰器本质上就是一个返回函数的高阶函数


下面就来看一下装饰器的使用案例:

# -*- coding: utf-8 -*-
def log(func):    #装饰器,接收一个函数,返回一个函数
    def wrapper(*args,**kw):
        func()  #这里调用了函数,因为没有定义参数,所以最后输出args= (),kw= {}
        print("name is %s()" % func.__name__)
        return func(*args,**kw)    #这里调用了传入的函数,也就是now函数,并且定义了参数,最后输出args= (1, 2, 3),kw= {'a': 'aaa'}
    print("aaa")
    return wrapper
@log    #@符号把装饰器log置于now函数定义处,相当于执行了"now = log(now)"
def now(*args,**kw):
    print("args= %s,kw= %s" % (args,kw))
if __name__=='__main__':   
    now(1,2,3,a="aaa")   #调用函数并且传入参数
    print(now.__name__)
#输出:
aaa
args= (),kw= {}
name is now()
args= (1, 2, 3),kw= {'a': 'aaa'}
wrapper
#解析
由于log()是一个装饰器,会返回一个函数,但是原来的now函数依然存在,上面只是把同名的now变量指向了新的函数,然后调用now执行新的函数,也就是在log函数中返回wrapper函数
wrapper函数的参数定义是(*args,**kw),这样定义参数可以使wrapper函数接受任意参数的调用

如果装饰器本身需要传入参数,那就需要编写一个返回装饰器的高阶函数

# -*- coding: utf-8 -*-
def log(test):
    def deco(func):
        def wrapper(*args,**kw):
            print("%s %s()" % (test,func.__name__))
            return func(*args,**kw)
        return wrapper
    return deco
@log('name is ')
def now():
    print("aaaaa")
if __name__=='__main__':
    now()
    print(now.__name__)
#输出:
name is  now()
aaaaa
wrapper
#解析:
其实看过上面的案例后,这里传入装饰器参数就很好理解了,“name is”赋值给了test参数,最终返回的还是wrapper函数,和上面的两层嵌套相比,这里的三层嵌套其实就是now = log('name is ')(now),log('name is ')返回的是装饰器函数deco,然后装饰器函数deco再调用now即deco(now),最终返回wrapper函数,先输出“name is  now()”,然后调用now函数输出”aaaaa“

看过上面的案例之后,可以看到最后一步输出print(now.__name__)时,输出了wrapper,now的函数名称从now变成了wrapper,这是因为装饰器返回的wrapper函数的函数名就是wrapper,所以,需要把原始函数now的__name__等属性复制到wrapper函数中,不然有些依赖函数签名的代码执行时就会报错


我们可以使用wrapper.__name__ = func.__name__这种代码来解决上面的问题,但是不需要,因为Python内置的functools.wraps就是做这个事情的,例如:

1、正常装饰器:
# -*- coding: utf-8 -*-
import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        print("name is %s()" % func.__name__)
        return func(*args,**kw)
    return wrapper
@log
def now(*args,**kw):
    print("%s %s" % (args,kw))
if __name__=='__main__':
    now(1,2,3,a='aaa')
    print(now.__name__)
#输出:
name is now()
(1, 2, 3) {'a': 'aaa'}
now
2、带参数的装饰器:
# -*- coding: utf-8 -*-
import functools
def log(test):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            print("%s is %s()" % (test,func.__name__))
            return func(*args,**kw)
        return wrapper
    return deco
@log('name')
def now(*args,**kw):
    print("%s %s" % (args,kw))
if __name__ == '__main__':
    now(1,2,3,a="ccc")
    print(now.__name__)
#输出:
name is now()
(1, 2, 3) {'a': 'ccc'}
now
可以看到,不管是带参数还是不带参数的装饰器,在加了@functools.wraps(func)后,最后输出now.__name__时,函数名称还是now
  • 这里引用一个小练习:
# -*- coding: utf-8 -*-
import time, functools
def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args,**kw):
        start_time = time.time()
        res = fn(*args,**kw)
        end_time = time.time()
        print('%s executed in %s ms' % (fn.__name__, end_time-start_time))
        return res
    return wrapper
# 测试
@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y;
@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f == 33 and s == 7986:
    print('测试成功')
#输出:
fast executed in 0.013970375061035156 ms
slow executed in 0.12478971481323242 ms
测试成功


二、偏函数——functools.partial


在上面的装饰器中,使用了模块functools,除了functools.wraps,还有很多功能,其中有一个就是偏函数(Partial function),注意,这里的偏函数和数学意义上的偏函数不一样


在定义函数时,通过设置参数的默认值,也就是设置默认参数,可以降低调用函数的难度,而偏函数也可以做到这一点,例如:

1、int()函数可以把字符串转换为整数,当只传入字符串时,int函数默认按照十进制转换,也可以加base参数,执行进制的转换,要注意,int转换需要是字符串
>>> int('12345') 
12345
>>> int('12345',base=8) 
5349
>>> int('12345',base=16) 
74565
>>> int('111000',2) 
56
2、如果需要转换大量的字符串为指定进制,那么我们可以定义一个函数
>>> def test(x,base=2):    
...     return int(x,base) 
... 
>>> test("11100") 
28
>>> test("111")   
7
3、而functools.partial就是帮助我们创建一个偏函数的,像上面那种情况,不需要我们自己定义函数,可以直接使用functools.partial创建一个新函数
>>> import functools
>>> test = functools.partial(int,base=2) 
>>> test("1111") 
15
>>> test("1111000") 
120
>>> test("1111000",base=10)   #可以看到,在调用test函数添加参数base后也是可以正常调用的,说明functools.partial只是定义了默认参数
1111000
4、在创建偏函数时,可以接收不同的参数,例如可变参数*args和关键字参数**kw
>>> test = functools.partial(int,base=2) 
>>> kw = {'base':10} 
>>> test("11111",**kw) 
11111
>>> test_1 = functools.partial(max,10) 
>>> args = (12,55,4)  
>>> test_1(*args)    
55


当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。


在内置函数或自定义函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。如果是自定义函数,我们可以定义默认参数,而内置函数在这种情况下,我们就可以使用偏函数,定义一个新的函数,并且指定默认参数


这里偏函数的作用其实就是在原函数的基础上创建一个新的函数,并且指定默认参数

太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。


在内置函数或自定义函数在执行时,要带上所有必要的参数进行调用。但是,有时参数可以在函数被调用之前提前获知。如果是自定义函数,我们可以定义默认参数,而内置函数在这种情况下,我们就可以使用偏函数,定义一个新的函数,并且指定默认参数


这里偏函数的作用其实就是在原函数的基础上创建一个新的函数,并且指定默认参数


目录
相关文章
|
1天前
|
数据安全/隐私保护 Python
探索Python中的装饰器:简化代码,提升效率
【9月更文挑战第32天】在Python编程世界中,装饰器是一个强大的工具,它允许我们在不改变函数源代码的情况下增加函数的功能。本文将通过直观的例子和代码片段,引导你理解装饰器的概念、使用方法及其背后的魔法,旨在帮助你写出更加优雅且高效的代码。
|
7天前
|
设计模式 缓存 测试技术
探索Python中的装饰器:从基础到高级应用
在本文中,我们将深入探讨Python中的装饰器,这是一种强大且灵活的工具,用于扩展或修改函数的行为。我们将从装饰器的基本概念和定义开始,逐步讲解它们的工作原理、如何创建和使用它们。接着,我们会探讨一些常见的装饰器用例,如日志记录、缓存和权限控制等。最后,本文将讨论一些高级话题,包括带参数的装饰器、使用functools模块增强装饰器以及装饰器与类方法的兼容问题。通过综合运用这些知识,您将能够更有效地利用Python的装饰器来优化您的代码。
21 10
|
5天前
|
Python
? Python 装饰器入门:让代码更灵活和可维护
? Python 装饰器入门:让代码更灵活和可维护
11 4
|
5天前
|
缓存 测试技术 Python
探索Python中的装饰器:简化代码,提高可读性
【9月更文挑战第28天】在Python编程中,装饰器是一个强大的工具,它允许我们在不修改原有函数代码的情况下增加额外的功能。本文将深入探讨装饰器的概念、使用方法及其在实际项目中的应用,帮助读者理解并运用装饰器来优化和提升代码的效率与可读性。通过具体示例,我们将展示如何创建自定义装饰器以及如何利用它们简化日常的编程任务。
10 3
|
5天前
|
Python
Python 装饰器入门:让代码更灵活和可维护
Python 装饰器入门:让代码更灵活和可维护
10 1
|
7天前
|
Python
探索Python编程中的装饰器魔法
【9月更文挑战第26天】在Python的世界里,装饰器就像是一把瑞士军刀,小巧而功能强大。它们让代码更简洁、可维护性更强。本文将通过实际示例,带你领略装饰器的魔力,从基础到进阶,一步步揭开它的神秘面纱。
12 2
|
7天前
|
设计模式 开发者 Python
Python中的装饰器:从入门到精通
【9月更文挑战第25天】本文深入浅出地介绍了Python装饰器的使用,包括其定义、语法和实际应用。通过实例演示如何利用装饰器增强函数功能,同时探讨了装饰器的高级用法如带参数的装饰器和装饰器嵌套。最后,文章强调了在设计装饰器时应避免的常见陷阱。
|
7天前
|
程序员 开发者 Python
探索Python中的装饰器:从基础到高级应用
本文旨在全面解析Python中一个强大而灵活的特性——装饰器(Decorators)。我们将从装饰器的基本定义出发,逐步深入到它们的高级应用。通过具体的代码示例和详细的解释,读者将能够掌握如何有效地使用装饰器来增强函数和类的功能,以及如何创建自定义装饰器来解决特定问题。无论是Python初学者还是经验丰富的开发者,都能在本文中找到有价值的内容,以提升编程技巧和代码质量。
13 1
|
7天前
|
设计模式 测试技术 开发者
探索Python中的装饰器:从基础到高级应用
本文旨在深入探讨Python中一个强大而灵活的特性——装饰器(Decorators)。通过逐步解析其工作原理、常见用法以及如何自定义装饰器,我们希望帮助读者不仅能够理解装饰器的基本概念,还能掌握如何在实际编程中高效利用这一工具来优化代码结构、增强程序的可读性和可维护性。无论是Python初学者还是经验丰富的开发者,都能在本文中找到有价值的信息和启示。
|
7天前
|
设计模式 Python
探索Python的装饰器:从基础到高级应用
在本文中,我们将深入探讨Python中的装饰器,这是一种强大而灵活的工具,可以修改或增强函数和类的行为。通过逐步解析其工作原理、常见用法以及一些高级技巧,我们希望帮助读者更好地理解和利用装饰器来编写简洁且高效的代码。
下一篇
无影云桌面