Python干货-装饰器与偏函数

装饰器

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

概括的讲,装饰器的作用就是为已经存在的函数或对象添加额外的功能。

举个简单的栗子:

# 定义一个记录日志用来调试的函数
def debug(func):
    def wrapper():
        print("[DEBUG]: enter {}()".format(func.__name__))
        return func()
    return wrapper

# 定义一个say_hello函数
def say_hello():
    print("hello!")

# 调用debug函数并将say_hello函数当作参数传入,返回的是wrapper函数
hello = debug(say_hello)
# 执行wrapper函数
hello()
[DEBUG]: enter say_hello()
hello!

debug函数对say_hello函数进行了包装,也就是增加了更多功能,实际上,debug函数已经是一个装饰器了

更加简单的写法是使用语法糖语法@debug,示例如下:

# 定义一个记录日志用来调试的函数
def debug(func):
    def wrapper():
        print("[DEBUG]: enter {}()".format(func.__name__))
        return func()
    return wrapper

@debug
# 定义一个say_hello函数
def say_hello():
    print("hello!")

say_hello()
[DEBUG]: enter say_hello()
hello!

@debug的效果与hello = debug(say_hello)是一样的,是不是很惊艳!

关于参数问题

定装饰器函数wrapper中调用了原函数say_hello,但如果原函数有参数,或者有很多参数的时候,可以利用python多参数形式,如下:

# 定义一个记录日志用来调试的函数
def debug(func):
    def wrapper(*args, **kwargs):
        print("[DEBUG]: enter {}()".format(func.__name__))
        return func(*args, **kwargs)
    return wrapper

@debug
# 定义一个say_hello函数
def say_hello(name, world):
    print(world, name)

say_hello('Jack', 'hello')
[DEBUG]: enter say_hello()
hello Jack

通过使用*args, **kwargs这得传参数形式,支持元组形式或字典形式的传值结构,不管是有参数还是多个参数,都可以很好的应对

带参数的装饰器

定义一个可以指定日志输出级别的“日志输出装饰器函数”

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print("[{level}]: enter function {func}()".format(
                level=level,
                func=func.__name__))
            return func(*args, **kwargs)
        return inner_wrapper
    return wrapper

@logging(level='INFO')
def say(something):
    print("say {}!".format(something))

# 如果没有使用@语法,等同于
# say = logging(level='INFO')(say)

@logging(level='DEBUG')
def do(something):
    print("do {}...".format(something))

say('hello')
do("my work")
[INFO]: enter function say()
say hello!
[DEBUG]: enter function do()
do my work...

解析:

当带参数的装饰器被打在某个函数上时,比如@logging(level=‘DEBUG’),它其实是一个函数,会马上被执行

而被执行的logging函数返回的结果,才是一个装饰器函数O(∩_∩)O哈哈~

偏函数

偏函数可以设定函数的默认行为,示例如下:

num = int('12345')
print(num)
12345

在默认情况下,int()函数会将字符串数字转换成十进制的数据,便也可以通过设定参数来让int()函数将其转换成八进制数字,如下:

num = int('12345', base=8)
print(num)
5349

那如何改变int()函数的默认行为呢?

# 首先导入 functools 模块
import functools

# 定义一个默认转换为二进制的int函数
int2 = functools.partial(int, base=2)
# 调用 
int2('10110101')
181

functools.partial的作用就是将固定一个函数的默认行为,从而简化之后的使用

来看看关于它的描述:

Init signature: functools.partial(self, /, *args, **kwargs)
Docstring:     
partial(func, *args, **keywords) - new function with partial application
of the given arguments and keywords.

所以,这个方法可以接收函数、*args、**kwargs这些对象

这里的base=2是字典结构的参数

max2 = functools.partial(max, 10)
max2(5, 6, 7)  # 相当于max2(10, 5, 6, 7)
10

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值