Python全栈开发

一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了。

一、装饰器

  装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),装饰器的功能非常强大,但是理解起来有些困难,因此我尽量用最简单的例子一步步的说明这个原理。

1、不带参数的装饰器

  假设我定义了一个函数f,想要在不改变原来函数定义的情况下,在函数运行前打印出start,函数运行后打印出end,要实现这样一个功能该怎么实现?看下面如何用一个简单的装饰器来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用@语法放在函数的定义上面 相当于执行 f=outer(f),此时f赋值成为了一个新的outer函数,
# 此时f函数就指向了outer函数的返回值inner,inner是一个函数名,定义在oute函数里面
# 原来的f是函数名可简单理解为一个变量,作为outer函数的参数传递进去了 此时参数func相当于f
def outer(func):                    # 定义一个outer函数作为装饰器
    def inner():            # 如果执行inner()函数的话步骤如下:
        print('start')              # 1、首先打印了字符‘start’,
        r=func()                    # 2、执行func函数,func函数相当于def f(): print('中')
        print('end')                # 3、接着函数打印‘end’
        return r                    # 4、将func函数的结果返回
    return inner
 
@outer
def f():              # f=outer(f)=innner
    print('中')
 
f()                   # f()相当于inner(),执行inner函数的步骤看上面定义处的注释<br>#打印结果顺序为   start 中 end

2、包含任意参数的装饰器

  在实际中,我们的装饰器可能应用到不同的函数中去,这些函数的参数都不一样,那么我们怎么实现一个对任意参数都能实现功能的装饰器?还记得我写函数那篇博客中,就写一种可以接受任意参数的函数,下面来看看如何将其应用到装饰器中去  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#其实只要将上面一种不带参数的装饰器修改一下就可以了
#修改也很简单,只需将inner和func的参数改为 (*args,**kwargs)
#其他实现的过程和上面一种一样,就不再介绍了
def outer(func):
    def inner(*args,**kwargs):
        print('start')
        r=func(*args,**kwargs)    # 这里func(*args,**kwargs)相当于f(a,b)
        print('end')
        return r
    return inner
 
@outer
def f(a,b):
    print(a+b)
f(1,4)                    # f(1,4)相当于inner(1,4) 这里打印的结果为 start 5 end

3、使用两个装饰器

  当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解使用@语法的时候到底执行了什么,是理解装饰器的关键。这里还是用最简单的例子来进行说明。  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def outer2(func2):
    def inner2(*args,**kwargs):
        print('开始')
        r=func2(*args,**kwargs)
        print('结束')
        return r
    return inner2
 
def outer1(func1):
    def inner1(*args,**kwargs):
        print('start')
        r=func1(*args,**kwargs)
        print('end')
        return r
    return inner1
 
@outer2                                # 这里相当于执行了 f=outer1(f)  f=outer2(f),步骤如下
@outer1                                #1、f=outer1(f) f被重新赋值为outer1(1)的返回值inner1,
def f():                               #    此时func1为 f():print('f 函数')
    print('f 函数')                     #2、f=outer2(f) 类似f=outer2(inner1) f被重新赋值为outer2的返回值inner2
                                       #    此时func2 为inner1函数 inner1里面func1函数为原来的 f():print('f 函数')
                                                                          
f()                                    # 相当于执行 outer2(inner1)()
>>开始                                  # 在outer函数里面执行,首先打印 ‘开始 ’
>>start                                # 执行func2 即执行inner1函数 打印 ‘start’
>>f 函数                               # 在inner1函数里面执行 func1 即f()函数,打印 ‘f 函数’
>>end                                  # f函数执行完,接着执行inner1函数里面的 print('end')
>>结束                                 # 最后执行inner2函数里面的 print('结束')

4、带参数的装饰器  

  前面的装饰器本身没有带参数,如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器,而且前面写的装饰器都不太规范,下面来写一个比较规范带参数的装饰器,下面来看一下代码,大家可以将下面的代码自我运行一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import functools
 
def log(k=''):                                        #这里参数定义的是一个默认参数,如果没有传入参数,默认为空,可以换成其他类型的参数
    def decorator(func):
        @functools.wraps(func)                        #这一句的功能是使被装饰器装饰的函数的函数名不被改变,
        def wrapper(*args, **kwargs):
            print('start')
            print('{}:{}'.format(k, func.__name__))    #这里使用了装饰器的参数k
            = func(*args, **kwargs)
            print('end')
            return r
        return wrapper
    return decorator
 
@log()                        # fun1=log()(fun1) 装饰器没有使用参数
def fun1(a):
    print(a + 10)
 
fun1(10)
# print(fun1.__name__)        # 上面装饰器如果没有@functools.wraps(func)一句的话,这里打印出的函数名为wrapper
 
@log('excute')                # fun2=log('excute')(fun2) 装饰器使用给定参数
def fun2(a):
    print(a + 20)
fun2(10)

  

Python全栈开发的更多相关文章

  1. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  2. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  3. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  4. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

  5. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  6. Python全栈开发【基础二】

    Python全栈开发[基础二] 本节内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典) 其他(编码,range,f ...

  7. Python全栈开发【基础一】

    Python全栈开发[第一篇] 本节内容: Python 的种类 Python 的环境 Python 入门(解释器.编码.变量.input输入.if流程控制与缩进.while循环) if流程控制与wh ...

  8. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  9. 老男孩最新Python全栈开发视频教程(92天全)重点内容梳理笔记 看完就是全栈开发工程师

    为什么要写这个系列博客呢? 说来讽刺,91年生人的我,同龄人大多有一份事业,或者有一个家庭了.而我,念了次985大学,年少轻狂,在大学期间迷信创业,觉得大学里的许多课程如同吃翔一样学了几乎一辈子都用不 ...

随机推荐

  1. mysql中怎样查看和删除唯一索引

    mysql中怎样查看和删除唯一索引. 查看唯一索引: show index from mytable;//mytable 是表名 查询结果例如以下: 查询到唯一索引后,怎样删除唯一索引呢,使用例如以下 ...

  2. Android学习——百度地图开发定位与显示Demo

    百度地图给我们提供了很丰富的API供我们进行二次开发.百度地图的SDK与定位SDK在今年6月份进行了更新. 地图更新为3.0,定位更新为4.2.百度说:这次更新对接口有了较大部分的调整,与之前版本号不 ...

  3. android FragmentPagerAdapter的“标准”配置

    private class ImagePagerAdapter extends FragmentPagerAdapter {           public List<ImageItem> ...

  4. CSS的三种样式:内联式,嵌入式,外部式以及他们的优先级

    从CSS 样式代码插入的形式来看基本能够分为下面3种:内联式.嵌入式和外部式三种. 1:内联式css样式表就是把css代码直接写在现有的HTML标签中,如以下代码: <p style=" ...

  5. [Swust OJ 352]--合并果子(贪心+队列模拟)

    题目链接:http://acm.swust.edu.cn/problem/352/ Time limit(ms): 1000 Memory limit(kb): 65535   Description ...

  6. 深入探究VC —— 链接器link.exe(4)

    在程序编译完成后,生成的文件是以.obj为扩展名的对象文件,link.exe是将这些对象文件与库链接起来以创建可执行文件或动态链接库文件的工具. link.exe的输入文件包括obj文件.lib文件. ...

  7. 安装基于XenServer的DevStack

    Openstack默认的hypervisior是基于KVM的,可以修改nova-compute.conf的libvirt_type改成使用其他,网上可以搜到个别文章 但是Openstack官方文档却说 ...

  8. Selenium Grid跨浏览器-兼容性测试

    Selenium Grid跨浏览器-兼容性测试 这里有两台机子,打算这样演示: 一台机子启动一个作为主点节的hub 和 一个作为次节点的hub(系统windows 浏览器为ie) ip为:192.16 ...

  9. H5前端面试题及答案(2)

    最近想着跳槽,但面试的邀约不多,内心有点烦躁.梳理梳理心情,跳槽季竞争也大,努力做好自己... 21.请设计一套方案,用于确保页面中js加载完全. <!doctype html> < ...

  10. javascript笔记整理(事件)

    一.事件驱动 1.事件javascript侦测到的用户的操作或是页面的一些行为(怎么发生的) 2.事件源引发事件的元素(发生在谁的身上) 3.事件处理程序对事件处理的程序或是函数 (发生了什么事) 二 ...