Python 高级编程 装饰器

1.1装饰器

装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,自学时直接绕过去了,然后面试问到了就挂了,因为装饰器是程序开发的基础知识,这个都不会,别跟人家说你会Python,看了下面的文章,保证你学会装饰器。

装饰器,功能就是在运行原来功能基础上,加上一些其它功能,比如权限的验证,比如日志的记录等等。不修改原来的代码,进行功能的扩展。

比如java中的动态代理,python的注解装饰器

其实python的装饰器,是修改了代码。

1.1.1装饰器理解

1、先明白这段代码

Python 高级编程 装饰器_第1张图片
Python 高级编程 装饰器_第2张图片

需求来了

初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

Python 高级编程 装饰器_第3张图片
Python 高级编程 装饰器_第4张图片

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

·封闭:已实现的功能代码块

·开放:对扩展开发

单独以fun1为例:

Python 高级编程 装饰器_第5张图片

python解释器就会从上到下解释代码,步骤如下:

1.def w1(func): ==>将w1函数加载到内存

2.@w1

没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

从表面上看解释器着实会执行这两句,但是@w1这一句代码里却有大文章,@函数名 是python的一种语法糖。

上例@w1内部会执行一下操作:

执行w1函数

执行w1函数 ,并将@w1下面的函数作为w1函数的参数,即:@w1等价于fun1 = w1(fun1)  fun1指向inner所以,内部就会去执行:

Python 高级编程 装饰器_第6张图片

w1的返回值

将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名fun1即将w1的返回值再重新赋值给fun1,即:

所以,以后业务部门想要执行fun1函数时,就会执行 新fun1函数,在新f1函数内部先执行验证,再执行原来的fun1函数,然后将原来fun1函数的返回值返回给了业务调用者。

如此一来,即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着。在函数内再加验证就好了。在这里我只简单演示一下,就不加验证判断了。

1.1.1多个装饰器

Python 高级编程 装饰器_第7张图片

如果装饰运行完毕之后,如果后面还有装饰器,交给下一个装饰器

直到没有装饰器了,执行功能代码

1.1.1装饰器(decorator)功能

1.引入日志

2.函数执行时间统计

3.执行函数前预备处理

4.执行函数后清理功能

5.权限校验等场景

6.缓存

1.1.1装饰器示例

例1:无参数的函数

Python 高级编程 装饰器_第8张图片

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

Python 高级编程 装饰器_第9张图片

例2:被装饰的函数有参数

Python 高级编程 装饰器_第10张图片

例3:被装饰的函数有不定长参数

Python 高级编程 装饰器_第11张图片

例4:装饰器中的return

Python 高级编程 装饰器_第12张图片

总结:

·一般情况下为了让装饰器更通用,可以有return

例5:装饰器带参数,在原有装饰器的基础上,设置外部变量

Python 高级编程 装饰器_第13张图片

例6:类装饰器(扩展,非重点)

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了__call__()方法,那么这个对象就是callable的。

Python 高级编程 装饰器_第14张图片

说明:

1.当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象

并且会把test这个函数名当做参数传递到__init__方法中

即在__init__方法中的func变量指向了test函数体

2.test函数相当于指向了用Test创建出来的实例对象

3.当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法

4.为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用

所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体

1.1python是动态语言

1.1.1动态语言的定义

动态编程语言是高级程序设计语言的一个类别,在计算机科学领域已被广泛应用。它是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。例如JavaScript便是一个动态语言,除此之外如PHP、Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。----来自维基百科

运行的过程中给对象绑定(添加)属性

Python 高级编程 装饰器_第15张图片

在这里,我们定义了1个类Person,在这个类里,定义了两个初始属性name和age,但是人还有性别啊!如果这个类不是你写的是不是你会尝试访问性别这个属性呢?

Python 高级编程 装饰器_第16张图片

这时候就发现问题了,我们定义的类里面没有sex这个属性啊!怎么回事呢? 这就是动态语言的魅力和坑! 这里 实际上就是 动态给实例绑定属性!

1.1.1运行的过程中给类绑定(添加)属性

Python 高级编程 装饰器_第17张图片

None          可以看到没有出现异常

给P这个实例绑定属性对P1这个实例不起作用! 那我们要给所有的Person的实例加上sex属性怎么办呢? 答案就是直接给Person绑定属性!

运行的过程中给类绑定(添加)方法

我们直接给Person绑定sex这个属性,重新实例化P1后,P1就有sex这个属性了! 那么function呢?怎么绑定?

Python 高级编程 装饰器_第18张图片

既然给类添加方法,是使用类名.方法名= xxxx,那么给对象添加一个方法也是类似的对象.方法名= xxxx

Python 高级编程 装饰器_第19张图片

1.1.1运行的过程中删除属性、方法

删除的方法:

1.del对象.属性名

2.delattr(对象, "属性名")

通过以上例子可以得出一个结论:相对于动态语言,静态语言具有严谨性!所以,玩动态语言的时候,小心动态的坑!

Python 高级编程 装饰器_第20张图片
删过之后就报错了


那么怎么避免这种情况呢?请使用__slots__,

1.1.1__slots__

现在我们终于明白了,动态语言与静态语言的不同

动态语言:可以在运行的过程中,修改代码

静态语言:编译时已经确定好代码,运行过程中不能修改

如果我们想要限制实例的属性怎么办?比如,只允许对Person实例添加name和age属性。只能限定实例对象的添加属性和方法

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

Python 高级编程 装饰器_第21张图片

注意:

·使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

你可能感兴趣的:(Python 高级编程 装饰器)