python简单装饰器_Python自学之路——自定义简单装饰器

看了微信公众号推送的一道面试题,发现了闭包的问题,学习时间短,从来没有遇到过这种问题,研究一下。

Python函数作用域

global:全局作用域

local:函数内部作用域

enclosing:函数与嵌套函数之间的作用域

build-in:内置作用域

1 #global

2 age = 60#全局变量

3

4 deffunc():5 age = 40

6 #对于func来说是函数内部变量,对于in_func来说是enclosing变量

7 print('func-->',age)8 age = age-40

9 print("func-->",age)10 print('%x' %id(age))11 defin_func():12 print('in_func-->',age)13 returnin_func14 f =func()15 f()16 print(f.__closure__)17 age = age-40#全局变量

18 print('global-->',age)

1 func--> 40

2 func-->03 65d0c6904 in_func-->05 (,)6 global--> 20

对于enclosing作用域,f = func() 就相当于f = in_func() 然后in_func()会运行,但是我之前的func函数已经运行完了按道理系统会回收func函数所定义的多有东西,为什么还会打印输出呢。

因为in_func函数被返回,不会被回收,引用计数不为零。在infunc函数输出时会查找本地,然后嵌套,然后全局。当查找到嵌套的变量是发现了age。

由于发现了enclosing变量,infunc函数就会把这个变量放到自己的属性中,代码运行结果的输出可以证明,infunc函数的闭包变量的地址和func函数的age属性的地址一模一样。

这个就可以称为闭包。也就是内部函数中对enclosing作用域的变量进行引用。

那么Python怎么创建闭包:

1、函数必须有内嵌函数

2、函数必须返回内嵌函数

3、内嵌函数必须引用enclosing作用域 的变量

那么接下来的装饰器就是闭包的一个典型的实现。。

装饰器的作用就装饰一个函数,通俗的讲就是对已有的函数进行功能上的拓展,但是不能改变被装饰函数的源代码,不能改变被装饰函数的调用方式。

因为可能你的代码已经被放到线上运行,或者你的函数已经被别的部门调用......种种原因吧

先看看简单例子:

1 deffoo():2 print("in the foo")3

4

5

6 foo()

deco_1

举例很简单就是运行了一个函数,输出为“in the foo”

假设我们现在有了一个需求,我们需要知道这个函数运行了多长时间,怎么办?

很简单嘛!加一个函数 timer ,将 foo 函数作为参数传进去就好了啊。

(为了体现运行效果,加入了睡眠3秒,所有这个不能算改变源代码哈)

1 importtime2

3

4 deffoo():5 time.sleep(3)6 print("in the foo")7 deftimer(func):8 start_time =time.time()9 func()10 stop_time =time.time()11 print ("run time :%s"%(stop_time-start_time))12

13 #foo()

14 timer(foo)

方法一

但是这个方法也不好啊,因为你改变了函数的调用方式啊

原来是   foo()   ,现在是  timer()  这个就有问题了。

于是我们就模仿上边的方法,写了嵌套函数,然后最终调用还是foo()

如下啦:

1 importtime2

3 deftimer(func):4 defwrapper():5 start_time =time.time()6 func()7 stop_time =time.time()8 print ("run time : %s"%(stop_time-start_time))9 returnwrapper10 deffoo():11 time.sleep(3)12 print("in the foo")13

14

15

16 foo =timer(foo)17 foo()

这段代码就是一个简单的没有参数的装饰器了。。。

逐行解读一下,当我们运行  foo = timer(foo) 时 timer函数先运行def wrapper 把wrapper函数刷入内存,但是并不会执行,然后return   wrapper函数名给foo,那么此时函数名wrapper就和函数名foo指向同一个内存地址了。这句话就结束了。

下边一句foo()就相当于wrapper()。然后运行wrapper函数。此时那个func参数,就是内存地址为之前的foo的那个地址,所以这句话就是wrapper函数运行调用了以前的foo函数。。。。

有点绕,当时学的时候可以说的一脸XX啊。在pycharm中加断点调试会看的更清晰。

但是Python觉得这么写代码还是多,于是搞出了一个叫  语法糖  的东西。。就是下边这个样子

1 importtime2

3 deftimer(func):4 defwrapper():5 start_time =time.time()6 func()7 stop_time =time.time()8 print ("run time : %s"%(stop_time-start_time))9 returnwrapper10

11

12 @timer#语法糖 其实就是相当于在调用foo函数前加了foo = timer(foo)这个代码

13 deffoo():14 time.sleep(3)15 print("in the foo")16

17

18

19 #foo = timer(foo)

20 foo()

顿时高大上了不少哈

简单装饰器就写完了,但是这个装饰器基本上没有什么卵用......

函数运行正常都有参数吧。。。

所以上边的都是铺垫,接下来进入正戏了:

模拟一个用户登录验证的小程序

1 defverify(func):2 defwrapper(username,passwd):3 if username == "sunqi" and passwd == "123456":4 print ('verify successful')5 func(username,passwd)6 else:7 print ('verify faild...')8 exit()9 returnwrapper10

11 @verify12 deflogin(username,passwd):13 print ("welcome!")14

15 if __name__ == '__main__':16

17 user = input("username >>:")18 password = input('password >>:')19 login(user,password)

后续还有带参数的装饰器

还有那个没懂得面试题……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值