Python3基础篇——闭包与装饰器

在开始学习之前,我们先来了解一下作用域的概念,接下来我们先来看下下面这段程序:

a = 0            # 定义了一个变量a

def func1():     # 定义了一个方法func1
    a = 1        # 将变量赋值为1

func1()         # 调用了方法func1
print(a)         # 将变量a输出了

# 输出结果:
# 0

我们发现变量a并没有被修改,这是因为func1()方法中的a和方法外的a并不是同一个a,这就像是在电影院看电影,有很多的位置,每个位置都是单独的,你在位置上吃东西还是干嘛,跟其他人没有关系,只要别影响别人就行;在我们程序里面也是一样的,每一个方法都是一个独立的个体,在执行的时候才会存在内存中,当方法执行完毕了,就像是电影看完了,就要离场了,总不能一直占着位置,这样这块内存空间就会释放;
那么我们怎么在方法的内部调用外部的变量呢?这时候我们可以用global导入全局变量:

def func1():
    global a
    a = 1

这样就相当于是买了两张票和朋友一起去看电影,那么另一个位置的吃的喝的也能够享用了;那么这个就是作用域的概念,只有在我的合法空间内才能做我们想做的事情。

 

那么接下来,我们再看一段程序:

def func1():
    print('func1 print')
    def func2():
        print('func2 print')
    func2()

func1()

# 输出结果:
# func1 print
# func2 print

这种写法我们之前没有见过,在方法里面我们又定义了一个方法,并且在func1()中我们直接调用了func2(),那么问题来了,我们能不能直接在方法外部调用func2呢?

我们可以看见程序运行报错了,提示func2没有定义,那么问题来了,为什么func1可以正常运行,func2就有问题呢?
这是因为方法都是先定义,后使用的;func1()能够被调用,是因为我在上面定义过了;按理当我运行完func1()之后,func2()不是也应该被定义了嘛,但是请注意,上面说过了,当方法执行完毕之后,占用的内存空间是会被释放掉的,那么func2()在func1()执行完毕之后就被释放了,func2也将不存在了,下面调用一个不存在的东西,肯定会报错啊!!!

那么,如果我就是想用func2该怎么办呢?

我们发现,可以将func2 return出来呀,注意这里的func2指的是函数对象,func2()是方法的调用,当我通过调用func1()这个方法将func2返回给func之后,func就相当于把这个方法单独提出来了,这个方法的所有数据和效果,通过func都可以实现;

OK,这个如果可以理解的话,我们接下来就要开始讲什么是闭包了,还是先看一段程序:

def func1():
    a = 1
    def func2():
        print(a)
    return func2

func = func1()
func()

# 输出结果:
# 1

我们观察上面的程序和运行结果,发现好像有悖于我们上面的说法,a不是外部的变量,这里也没有通过global导入,我在func2()中怎么能直接使用呢?那么这只有一种可能,当func1()执行完毕后,所有的内容都消失,这是一定的,不然我们print(a)应该就有结果输出;那么这个a就只能被存放在了func中,我们来实验一下:

def func1(obj):
    print(obj)
    def func2():
        obj[0] += 1
        print(obj)
    return func2

func = func1([0,1,2,3])
func()
func()
func()

# 输出结果:
# [0, 1, 2, 3]
# [1, 1, 2, 3]
# [2, 1, 2, 3]
# [3, 1, 2, 3]

我们通过外部传参,传进了一个列表,我们在func2中对列表的第一个数字进行了修改,我们发现后面连续的三个func(),这个列表还是存在的,这就说明了func不仅对func2()这个方法进行了存储,还对与他有关的变量进行了存储;
那么现在我们可以总结下,一个方法定义中引入了这个函数以外的变量,并且这个方法在脱离了其定义的环境下还可以被正常执行,这个函数就叫闭包函数。

OK,那么接下来我们就看下什么是装饰器?

def func(func):
    def func1():
        print('已经调用了装饰器:')
        return func()
    return func1()

@func
def func2():
    print('func2 print')

# 已经调用了装饰器:
# func2 print

上面就是一个简单的装饰器的应用,我们可以先看一下效果:

  1. 第一行输出了:“已经调用了装饰器”,我们发现这段话是在func()中的func1()方法中输出的
  2. 第二行则是在func2()中输出的

我们发现这是两个半毛钱没有关系的方法,怎么就一起被执行调用了呢?而且我们也没有发现有任何调用的语句啊?还有一个奇怪的东西,@func是个什么东西?
细心的朋友可能发现了,@func貌似起到了一个承上启下的作用,它貌似是一个桥梁,将两个函数连接起来了,它就是装饰器的一个应用;我们接下来来看一下它到底做了些什么事情:

  1. 首先先输出了“已经调用了装饰器”,那么肯定执行了func1()中的内容,通过上面的学习,我们知道要想调用func1()肯定是:f = func() -> f(),那么我们合并一下就是:func()()
  2. 然后才是执行func2()的内容,我们发现func()方法是有一个参数的,通过函数体我们可以知道,这个参数其实就是一个函数对象,因为在func1()中被调用了
  3. 那么我们再来整理一下,就可以得到:func(func2)(),最后的一个执行过程也是这样的执行过程,那么这样就能解释上面的问题了;

这里还有一个问题,我在上面为什么要return func()呢?我直接func()不行吗?当然可以,但是需要注意的是,这种情况在没有返回值的时候可以直接这么写,如果有返回值的话,就缺少了返回返回值这一步;也就是说return func()其实是将func()这个方法的结果进行返回了;

 

最后我们思考一个问题,装饰器可以接受参数嘛?

def func(sta):
    def func1(func_):
        def func2():
            if sta:
                print('状态正常!')
            else:
                print('状态不正常!')
            return func_()
        return func2
    return func1

@func(True)
def func_t():
    print('机器正常运行!')

@func(False)
def func_f():
    print('机器出现故障!')

func_t()
func_f()

当然可以,其实也是一样的道理,这里就不赘述了!
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值