python -- (闭包、装饰器原理) --(六)

闭包

闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

# 定义一个函数
def test(number):
    # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
    def test_in(number_in):
        print("in test_in 函数, number_in is %d" % number_in)
        return number + number_in

    # 其实这里返回的就是闭包的结果
    return test_in


# 给test函数赋值,这个20就是给参数number
ret = test(20)

# 注意这里的100其实给参数number_in
print(ret(100))

# 注 意这里的200其实给参数number_in
print(ret(200))

运行结果:
在这里插入图片描述

修改外部函数中的变量

一般在函数结束时,会释放临时变量,但在闭包中,由于外函数的临时变量在内函数中用到,此时外函数会把临时变量与内函数绑定到一起,这样虽然外函数结束了,但调用内函数时依旧能够使用临时变量,即闭包外层的参数可以在内存中进行保留
如果想要在内函数中修改外函数的值,需要使用 nonlocal 关键字声明变量

def counter(start=0):
    def incr():
        nonlocal start
        start += 1
        return start

    return incr


c1 = counter(5)
print(c1())
print(c1())

c2 = counter(50)
print(c2())
print(c2())

print(c1())
print(c1())

print(c2())
print(c2())

运行结果
在这里插入图片描述

装饰器

在不用修改原函数代码情况下,就可以给原函数(在函数整体之前或之后)添加新功能(不能实现在原函数内部添加新功能),并且在调用的时候和原来调用一模一样,这里需要注意的是,装饰器是基于闭包环境存在的。

# 定义函数:完成包裹数据
def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"

    return wrapped


# 定义函数:完成包裹数据
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"

    return wrapped


@makeBold
def test1():
    return "hello world-1"


@makeItalic
def test2():
    return "hello world-2"


@makeBold
@makeItalic
def test3():
    return "hello world-3"


print(test1())
print(test2())
print(test3())

运行结果:
在这里插入图片描述

装饰器功能:
  • 引入日志
  • 函数执行时间统计
  • 执行函数前预备处理
  • 执行函数后清理功能
  • 权限校验等场景
  • 缓存
无参数的函数
from time import ctime, sleep

def timefun(func):
    def wrapped_func():
        print("%s called at %s" % (func.__name__, ctime()))
        func()
    return wrapped_func

@timefun
def foo():
    print("I am foo")

foo()
sleep(2)
foo()

上面代码理解装饰器执行行为可理解成:

foo = timefun(foo)
# foo先作为参数赋值给func后,foo接收指向timefun返回的wrapped_func
# 调用foo(),即等价调用wrapped_func()
# 内部函数wrapped_func被引用,所以外部函数的func变量(自由变量)并没有释放
# func里保存的是原foo函数对象
被装饰的函数有参数
from time import ctime, sleep


def timefun(func):
    def wrapped_func(a, b):
        print("%s called at %s" % (func.__name__, ctime()))
        print(a, b)
        func(a, b)
    return wrapped_func


@timefun
def foo(a, b):
    print(a+b)


foo(3,5)
sleep(2)
foo(2,4)
被装饰的函数有不定长参数
from time import ctime, sleep


def timefun(func):
    def wrapped_func(*args, **kwargs):
        print("%s called at %s"%(func.__name__, ctime()))
        func(*args, **kwargs)
    return wrapped_func


@timefun
def foo(a, b, c):
    print(a+b+c)


foo(3,5,7)
sleep(2)
foo(2,4,9)
装饰器中的return
from time import ctime, sleep


def timefun(func):
    def wrapped_func():
        print("%s called at %s" % (func.__name__, ctime()))
        func()
    return wrapped_func


@timefun
def foo():
    print("I am foo")


@timefun
def getInfo():
    return '----hahah---'


foo()
sleep(2)
foo()


print(getInfo())

运行结果:
在这里插入图片描述
如果修改装饰器为return func(),则运行结果:
在这里插入图片描述
一般情况下为了让装饰器更通用,可以有return

装饰器带参数,在原有装饰器的基础上,设置外部变量
from time import ctime, sleep


def timefun_arg(pre="hello"):
    def timefun(func):
        def wrapped_func():
            print("%s called at %s %s" % (func.__name__, ctime(), pre))
            return func()
        return wrapped_func
    return timefun


# 下面的装饰过程
# 1. 调用timefun_arg("itcast")
# 2. 将步骤1得到的返回值,即time_fun返回, 然后time_fun(foo)
# 3. 将time_fun(foo)的结果返回,即wrapped_func
# 4. 让foo = wrapped_fun,即foo现在指向wrapped_func
@timefun_arg("itcast")
def foo():
    print("I am foo")


@timefun_arg("python")
def too():
    print("I am too")


foo()
sleep(2)
foo()

too()
sleep(2)
too()

可以理解为

foo()==timefun_arg("itcast")(foo)()

---------------------------------------------------END-----------------------------------------------
---------------------------------------------------END-----------------------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值