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可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。


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


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


目录
相关文章
|
17天前
|
开发者 Python
探索Python中的装饰器:从基础到高级应用
本文将带你深入了解Python中的装饰器,这一强大而灵活的工具。我们将一起探讨装饰器的基本概念,它们如何工作,以及如何使用它们来增强函数和类的功能,同时不改变其核心逻辑。通过具体代码示例,我们将展示装饰器的创建和使用,并探索一些高级应用,比如装饰器堆栈和装饰带参数的装饰器。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角,帮助你更有效地使用装饰器来简化和优化你的代码。
|
18天前
|
测试技术 数据安全/隐私保护 开发者
探索Python中的装饰器:从基础到高级应用
装饰器在Python中是一个强大且令人兴奋的功能,它允许开发者在不修改原有函数代码的前提下增加额外的功能。本文将通过具体代码示例,带领读者从装饰器的基础概念入手,逐步深入到高级用法,如带参数的装饰器和装饰器嵌套等。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧。
|
18天前
|
开发框架 数据建模 中间件
Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器是那些静悄悄的幕后英雄。它们不张扬,却能默默地为函数或类增添强大的功能。本文将带你了解装饰器的魅力所在,从基础概念到实际应用,我们一步步揭开装饰器的神秘面纱。准备好了吗?让我们开始这段简洁而富有启发性的旅程吧!
26 6
|
6天前
|
缓存 数据安全/隐私保护 Python
python装饰器底层原理
Python装饰器是一个强大的工具,可以在不修改原始函数代码的情况下,动态地增加功能。理解装饰器的底层原理,包括函数是对象、闭包和高阶函数,可以帮助我们更好地使用和编写装饰器。无论是用于日志记录、权限验证还是缓存,装饰器都可以显著提高代码的可维护性和复用性。
20 5
|
17天前
|
测试技术 开发者 Python
探索Python中的装饰器:从入门到实践
装饰器,在Python中是一块强大的语法糖,它允许我们在不修改原函数代码的情况下增加额外的功能。本文将通过简单易懂的语言和实例,带你一步步了解装饰器的基本概念、使用方法以及如何自定义装饰器。我们还将探讨装饰器在实战中的应用,让你能够在实际编程中灵活运用这一技术。
36 7
|
16天前
|
Python
探索Python中的装饰器:简化代码,增强功能
在Python的世界里,装饰器就像是给函数穿上了一件神奇的外套,让它们拥有了超能力。本文将通过浅显易懂的语言和生动的比喻,带你了解装饰器的基本概念、使用方法以及它们如何让你的代码变得更加简洁高效。让我们一起揭开装饰器的神秘面纱,看看它是如何在不改变函数核心逻辑的情况下,为函数增添新功能的吧!
|
17天前
|
程序员 测试技术 数据安全/隐私保护
深入理解Python装饰器:提升代码重用与可读性
本文旨在为中高级Python开发者提供一份关于装饰器的深度解析。通过探讨装饰器的基本原理、类型以及在实际项目中的应用案例,帮助读者更好地理解并运用这一强大的语言特性。不同于常规摘要,本文将以一个实际的软件开发场景引入,逐步揭示装饰器如何优化代码结构,提高开发效率和代码质量。
42 6
|
16天前
|
存储 缓存 Python
Python中的装饰器深度解析与实践
在Python的世界里,装饰器如同一位神秘的魔法师,它拥有改变函数行为的能力。本文将揭开装饰器的神秘面纱,通过直观的代码示例,引导你理解其工作原理,并掌握如何在实际项目中灵活运用这一强大的工具。从基础到进阶,我们将一起探索装饰器的魅力所在。
|
17天前
|
测试技术 开发者 Python
深入理解Python装饰器:从基础到高级应用
本文旨在为读者提供一个全面的Python装饰器指南,从其基本概念讲起,逐步深入探讨其高级应用。我们将通过实例解析装饰器的工作原理,并展示如何利用它们来增强函数功能、控制程序流程以及实现代码的模块化。无论你是Python初学者还是经验丰富的开发者,本文都将为你提供宝贵的见解和实用的技巧,帮助你更好地掌握这一强大的语言特性。
29 4
|
18天前
|
开发者 Python
Python中的装饰器:从入门到实践
本文将深入探讨Python的装饰器,这一强大工具允许开发者在不修改现有函数代码的情况下增加额外的功能。我们将通过实例学习如何创建和应用装饰器,并探索它们背后的原理和高级用法。
34 5
下一篇
DataWorks