Python闭包和装饰器

闭包

  • 闭包
在嵌套函数中引用了自由变量的函数。这个自由变量就是外层嵌套函数中的变量(非全局变量)
  • 闭包必须满足三个条件
1. 必须是嵌套函数
2. 内层嵌套函数必须引用了外层嵌套函数的变量
3. 外层嵌套函数的返回值是内层嵌套函数
  • 作用
嵌套函数的内层函数可以使用外层函数的变量,即使外层函数返回了,或者被删除了。内层函数依然可以使用外层函数的那个变量。
  • 示例
# encoding:utf-8
# 判断闭包的方法closure,若果是None就不是闭包,如果有cell元素就是闭包
def func1():
	num1 = 10
	def func2():
		pass
	print("func2:", func2.__closure__)
	return func2
print(func1())
# output: func2: None, 因为内层嵌套函数必须引用了外层嵌套函数的变量

def func1():
	num1 = 10
	def func2():
		print(num1)
		return num1
	print("func2:", func2.__closure__)
	return func2
func1()
# output func2: (<cell at 0x0000024E3EE70EB8: int object at 0x000000005B6A6D40>,)

# 闭包,fn使用了变量y并且外层函数make_power的返回值是fn(内层嵌套函数)
# 这个闭包可以实现x的y次方
def make_power(y):
    # 自由变量就是y,当函数make_power返回后,fn依旧可以使用y的值
    def fn(x):
        return x * y
    return fn

res = make_power(10)
print(res(2))
#output:20

装饰器函数

  • 概述
简单来说,装饰器本质上就是闭包函数。一般就是传入一个函数或类,在不修改原来的函数以及其调用方式的前提下为原来的函数增加新的功能。通常的做法是,在装饰器函数的内层函数中调用被装饰的函数然后在被装饰的函数调用前面或者后面加上我们要执行的代码,达到扩展其功能的目的。比较常用的场景就是日志插入,事务处理。其实装饰器函数本质上就是函数名和函数地址的重新绑定。被装饰的函数虽然看起来和原来的函数名字是一样的,但是其在内存中已经修改了绑定关系,它已经绑定到我们装饰器函数的内层函数。
  1. 一个初始的函数

    如果想要记录函数的执行时间,最原始的做法是入侵到原来的函数中进行修改

import time
# 计算一个函数的运行的时间,最暴力的方法
def func():
    startTime = time.time()

    print("hello")
    time.sleep(1)
    print("world")

    endTime = time.time()

    seconds = endTime - startTime
    print("函数运行了{}秒".format(seconds))

if __name__ == '__main__':
    func()
  1. 如果不想入侵到原来的代码,可以将函数作为参数传入到一个具有计时功能的函数中
import time
#不改变原函数的代码的情况下统计函数执行所需的时间
def myfunc():
    print("hello")
    time.sleep(1)
    print("world")

#不改变原来的函数功能,但是每次都要执行该函数
def deco(func):
    startTime = time.time()
    # 运行函数
    func()
    endTime = time.time()

    seconds = endTime - startTime
    print("func()执行了{}秒".format(seconds))

if __name__ == '__main__':
    f = myfunc;
    #这里要统计时间的时候,每次都要调用deco()函数
    deco(f)

这样有一个问题就是每次都要调用deco()函数来实现计时

  1. 使用装饰器,既不改变原来函数的功能,又不需要重复调用deco()函数
import time

def deco(func):
	def wrapper():
		startTime = time.time()
		# 运行函数
		func()
		endTime = time.time()
		seconds = endTime - startTime
		print("func()执行了{}秒".format(seconds))
	return wrapper

@deco
def func1():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
	func1()

这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

目前存在一个问题,装饰器可能也会被其他类型得函数调用,函数传参可能会不一样,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

  • 装饰器的固定格式(接收任意的参数)
# 标注固定格式
def deco(func):
    def inner(*args,**kwargs):
        '''执行函数之前要做的'''
        re = func(*args,**kwargs)
        '''执行函数之后要做的'''
        return re
    return inner
  • 示例
import time

def timer(func):
    def inner(*args,**kwargs):
        startTime = time.time()
        # 运行函数
        re = func(*args, **kwargs)
        endTime = time.time()
        seconds = endTime - startTime
        print("func()执行了{}秒".format(seconds))

        '''执行函数之后要做的'''
        return re
    return inner
@timer
def func(a, b, c):
    time.sleep(1)
    return a, b, c

@timer
def func1(num):
	res = []
	for i in range(num):
		res.append(i)
	return res

print(func(1,2,3))
#output:func()执行了1.000331163406372秒
(1, 2, 3)
print(func1(3))
#output:
func()执行了0.0[0, 1, 2]
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值