python闭包与装饰器

闭包与装饰器

闭包的本质是函数嵌套,装饰器就是对闭包的应用。

1. 通过一个函数调用另一个函数

def test1 (*args,**kwargs):
    print("test1")

def test2(fun,*args,**kwargs):
    fun(*args, **kwargs)
    print("test2")

test2(test1)
print("-----------------------------")

def test4(*args,**kwargs):
    def test3(*args,**kwargs):
        print("test3")
    test3(args,kwargs)
    print("test4")

test4()

# 通过一个函数调用另一个函数的一些方法

2.闭包的格式

def 外层函数():
    def 内层函数():
        pass
    return 内层函数

内层函数引用 = 外层函数()
内层函数引用()

3.闭包与函数之间的区别

def out(year):
    def inner():
        print("内层函数调用",year)
    return inner
inner_func = out(2019)
inner_func()
inner_func()
print("--------------------------------")
def func(year):
    print("内层函数调用",year)
func(2020)
  • 闭包和函数的格式不一样
  • 外层函数可以在内存中进行保留
  • 因为内层函数被引用以后,整个外层函数就不会被回收,外层函数的变量也可以得到保存,缺点是占内存

4.内层函数修改外层函数的数据

  • nonlocal 声明外层函数
def out_func(year):
    def inner_func():
        nonlocal year  # 内层函数无法直接修改外层函数,需要通过nonlocal 声明
        year += 1
        print("得到外层函数的参数",year)
     return inner_func
func = out_func(2019)
func()

5.装饰器

def set_func(func):
    def call_func(*args,**kwargs):
        print('额外功能')
        return func(*args,**kwargs)
    return call_func
# @set_func相当于 test = set_func(test)  
@set_func
def test(*args,**kwargs):
    print("核心代码")
    
test()
权限验证
核心代码
  • 装饰器就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数
  • 装饰器不修改已有函数的代码,也不修改已有函数的调用方式
  • 闭包函数有且只有一个参数,而且必须是函数类型,这样定义的函数才是装饰器
  • 装饰器实现了即开放又封闭的写代码原则,它规定已实现的功能代码无法被修改,但可以被扩展
  • @set_func 是装饰器的语法糖,相当于执行test = set_func(test) 操作,是哟个语法糖就可以方便直观的实现对已有函数的装饰

6.装饰器的简单应用场景

# 已有功能函数可以从0开始输出,一直输出到99999

def num1():
    for i in range(100000):
        print(i)

num1()
# 现在想为这个函数添加计算执行时间功能
# 传统方式1,这种方式会直接修改函数
import time
def num1():
    start = time.time()
    for i in range(100000):
        print(i)
    end = time.time()
    print("执行时间:%s"%(end-start))
num1()
# 传统方式2,这种方式不会直接修改函数,但如果要在多个地方使用此功能会造成大量的代码冗余
import time
def num1():
    for i in range(100000):
        print(i)
start = time.time()  
num1()
end = time.time()
print("执行时间:%s"%(end-start))

# 传统方式3,这种方式会使原函数的调用方式改变,在实际工作中会造成大量的工作量
import time
def num1():
    for i in range(100000):
        print(i)
        
def get_time():
    start = time.time()  
	num1()
	end = time.time()
	print("执行时间:%s"%(end-start))
    
get_time()

# 此时可以使用装饰器来实现这种既不修改原函数,也不修改调用原函数的方式,还不会造成代码冗余的功能
import time
def set_func(func):
    def call_func(*args,**kwargs):
		start = time.time()  
		func()
		end = time.time()
        print("执行时间:%s"%(end-start))
    return call_func

@set_func
def num1():
    for i in range(100000):
        print(i)

num1()

7.通用装饰器

def set_func(func):
	def call_func(*args,**kwargs):
        # 额外功能
        return func(*args,**kwargs)
    return call_func

@set_func
def test(*args,**kwargs):
    # 原功能
  • 通用装饰器可以在无论被装饰函数有没有参数,有没有返回值的情况下使用

8.一个装饰器装饰多个函数

def set_fun1(func1):

	def call_fun1(*args, **kwargs):
		print("用户密码验证")
		return func1(*args, **kwargs)
	return call_fun1


def set_fun2(func2):

	def call_fun2(*args, **kwargs):

		print("短信验证")
		return func2(*args, **kwargs)
	return call_fun2

# 装饰器一定是被装饰的函数先初始化,离被装饰函数最近的装饰器先初始化
# 1.先走@set_fun2 == > test = set_fun2(test) => test = call_fun2
# 2.再走@set_fun1 ==> test = set_fun1(test) => test = call_fun1


# @set_fun2 ==> test = set_fun2(test) ==> test = call_fun2
# test = call_fun2 ,func2 = "def test"
#@set_fun1 == > test = set_fun1(test) == > test = call_fun1

# test = call_fun1, func1 = call_fun2

# 内层函数执行的顺序是从上向下
@set_fun1
@set_fun2
def test(*args,**kwargs):
	print("转帐")
	print(args)
	print(kwargs)

test(2,a=3)

9.带有参数的装饰器

# 添加输出日志的功能
def logging(flag):

    def decorator(fn):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法计算--")
            elif flag == "-":
                print("--正在努力减法计算--")
            result = fn(num1, num2)
            return result
        return inner

    # 返回装饰器
    return decorator


# 使用装饰器装饰函数
@logging("+") # @logging("+")-->return decorator-->@decorator
def add(a, b):
    result = a + b
    return result


@logging("-")
def sub(a, b):
    result = a - b
    return result

result = add(1, 2)
print(result)

result = sub(1, 2)
print(result)
  • 在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。
  • 使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用

10.类装饰器

class Check(object):
    def __init__(self, fn):
        # 初始化操作在此完成
        self.__fn = fn

    # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
    def __call__(self, *args, **kwargs):
        # 添加装饰功能
        print("请先登陆...")
        self.__fn()


@Check
def comment():
    print("发表评论")


comment()
  • @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
  • 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
  • call方法里进行对fn函数的装饰,可以添加额外的功能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值