引言
今天文章的标题,初读起来可能有些拗口,什么叫“为函数添加方法”?但是,如果真正对“Python函数也是对象”这个理念有清晰的理解的话,其实,也是不难理解的,本质上就是给一个对象新增一个自定义方法。通过这样做,我们就可以实现在运行过程中,对装饰器的属性进行动态修改了。
本文的主要内容有:
1、函数对象添加自定义方法
2、属性可变的动态装饰器
函数对象添加自定义方法
其实,这一点比较简单,只需要把函数当作Python中的一个普通对象即可。但是,如果一直执着于函数本身,则理解起来,会有些拧巴。
我们来演示下给函数添加自定义方法的做法,直接看代码:
def add(a, b):
base = 0
if hasattr(globals()['add'], 'base'):
base = globals()['add'].base
print('执行加法运算')
return base + a + b
def change_base(x):
globals()['add'].base = x
print(f'变更后:add.base={x}')
print(add(10, 20))
print('=' * 20)
add.change_base = change_base
add.change_base(100)
print('=' * 20)
print(add(10, 20))
执行结果:
首先要说明的,这是一个很拧巴的把函数当作普通对象进行使用的演示代码。
可以看到,我们给函数对象添加了一个change_base()的方法,用于修改add函数对象的base属性(当然,如果不存在base属性,就会动态添加一个base属性)。
属性可变的动态装饰器
既然在Python中一个函数对象可以当作普通对象使用,动态添加属性和方法都是可以的。那么,我们基于这样的特性,就可以进一步扩充作为装饰器的闭包函数对象,实现动态修改闭包属性的效果。
假设有这样一个业务需求,还是基于免费用户、普通vip、超级vip的用户的区分。免费用户转换为vip/svip门槛有些高,我们为了提高营收,增加了这样一项功能:支持免费用户按次付费,体验vip、svip的功能体验,从而增加付费用户的转化率。
这个业务需求,就需要我们在免费用户单次付费后,动态调整延迟时长了,也就是我们本文的主角:属性可变的动态装饰器。
我们稍微简化一下,之前的代码,简单定义一个延迟的装饰器,然后模拟付费体验一次的场景实现:
import time
def wait(func):
paid = False
def wrap(*args, **kwargs):
nonlocal paid
if paid:
print('使用专属付费渠道')
paid = False
else:
print('免费用户排队中...')
time.sleep(5)
return func(*args, **kwargs)
def pay_once():
nonlocal paid
paid = True
wrap.pay_once = pay_once
return wrap
@wait
def business():
print("开展正常业务")
if __name__ == '__main__':
print('付费体验前:')
business()
print('=' * 20)
print('付费体验一次vip:')
business.pay_once()
business()
print('=' * 20)
print('付费体验一次vip用完后:')
business()
执行结果:
从执行结果,可以看到,已经模拟出了付费体验一次vip功能,之后立马又是免费的排队模式的场景。
值得注意的点有:
1、由于后续访问的都是闭包内部的函数对象,也就是wrap,所以,定义了pay_once()的方法,也是要绑定到wrap对象的,所以,才会有:wrap.pay_once = pay_once这行代码,不然,根本没法调用pay_once()方法。
2、这个装饰器通过有点拧巴的组合实现了一个修改闭包状态、使用闭包状态的功能的组合,wrap本身用于访问闭包中的自由变量paid,pay_once()用于修改paid。
3、在内部函数中要修改闭包中的自由变量paid,需要首先通过nonlocal关键字进行声明,否则会被Python解释器当作再内部函数定义一个局部变量paid。
从效果来看,本身就是通过闭包的使用,在模拟实现普通对象的对属性的读写操作的组合。虽然有些拧巴,但是,如果已经掌握了装饰器、闭包的概念,理解起来,也是比较简单的。
总结
本文通过实例代码再次验证了Python中的函数也是对象的理念,同时,明确了函数不是只能作为可调用对象进行函数调用的操作,也可以像普通对象一样,进行动态属性、方法的添加。最后,通过函数对象动态添加方法的机制,实现了一个属性可变的装饰器,实现更加灵活的业务场景需求。
感谢您的拨冗阅读,希望对您有些许帮助!