一. 装饰器

装饰器:可以拆解来看,器本质就是函数,装饰就是修饰的意思,所以装饰器的功能就是为其他函数添加附加功能。

装饰器的两个原则:

1. 不修改被修饰函数的源代码

2. 不修改被修饰函数的调用方式

实现装饰器的知识储备: 装饰器 = 高阶函数 + 函数嵌套 + 闭包

高阶函数

高阶函数的定义:

1. 函数接收的参数是一个函数名

2. 函数的返回值是一个函数名

3.满足上述条件任意一个,都可以称之为高阶函数

import  time
# 示例1: 函数接收的参数是一个函数名
# def foo():
# print("Hello")
#
# def test(func):
# print(func)
# func()
#
# test(foo) # 示例2:函数test接收的参数是一个函数名foo,可以直接为foo函数增加一个功能
# 需求:想统计foo运行的时间,结果这个高阶函数修改了函数的源代码 # def foo():
# time.sleep(2)
# print("Hello")
#
# def test(func):
# print(func)
# start_time = time.time()
# func()
# stop_time = time.time()
# print('函数的运行时间是:%s '%(stop_time-start_time)) # 统计的是func()的运行时间,即foo的运行时间
#
# test(foo) # 示例3:函数的返回值是一个函数名 def foo():
print("from the foo")
def test(func):
return func # 实现了装饰器的不改变函数的调用方式这个功能
foo = test(foo)
foo() # 示例4: 示例2+示例3就能实现一个半完整的装饰器
def foo():
time.sleep(3)
print('来自foo') # 不修改foo源代码
# 不修改foo调用方式
# 没有修改被修饰函数的源代码,也没有修改被修饰函数的调用方式,但是也没有为被修饰函数添加新功能
def timer(func):
start_time = time.time()
func()
stop_time = time.time()
print('函数的运行时间是:%s '%(stop_time-start_time)) # 统计的是func()的运行时间,即foo的运行时间
return func
foo = timer(foo) # timer函数的返回值是foo,在把返回值又赋值给了foo,得到一个内存地址
foo() # 有了内存地址,直接加个()就可以运行。
# 结果
# 来自foo
# 函数的运行时间是:3.001082420349121
# 来自foo # 上面说的是半完整的装饰器,是因为这里多运行了一次foo,肯定不行那,这就导致这个装饰器就不合格了

函数嵌套

# 函数嵌套及作用域
# father()函数里嵌套了一层son()函数,son()函数里又嵌套了一层grandson()函数 def father(name):
print('from father %s' %name) # name变量的作用域,是传进来的DDD
def son(): # 函数也是变量
name='abc'
print('我的爸爸是%s' %name) # 作用域是son函数自己定义的name变量
def grandson():
print('我的爷爷是%s' % name) # grandson函数没有自己的name变量,需要往上一级一级找,找到了son函数,就是abc
# 同调用son的道理一样,需要通过father调用son,通过son函数再调用grandson函数
grandson()
# 如果要调用son函数,不能跑到father的外面去调用,会报错;只能通过调用father来调用son函数
# 此处是调用son函数
son()
father('DDD')
son('abcx') # 报错 # son函数定义在father里,所以只能通过father调用son函数

闭包

上面理解了函数的层级嵌套,就很好理解闭包的概念。嵌套的函数换个名称叫闭包

包:就像一个盒子套着一个盒子,一层层往外套,每一次都是一个包

闭:就是封装,封装变量。每个包都有被封装的变量。

下面画图解释

闭包如何找变量?

还是先从自己的闭包里开始找变量,自己没有在往上一层包里找变量,如果还没有,可以一直往外找,可以一直找到最外层

 装饰器基本实现

首先,
# 搭个装饰器的架子,满足高阶函数,满足函数嵌套
import time
def timer(func):
def wrapper():
# print(func) # 嵌套函数也可以获取传入的参数,传入的参数也是个函数,作用域在起作用 这是个内存地址
func() # 有了内存地址,加()就可以执行func函数
return wrapper def test():
time.sleep(2)
print("test函数运行完毕") 其次,
有个需求:不修改test函数的原代码,也不修改test函数的调用方式,而且为test函数增加一个统计运行时间的功能 实现该需求的流程分析及代码运行过程: # 流程分析:
# 1. 把test函数传给timer,直接返回的是wrapper的地址,而没有运行wrapper函数
res = timer(test)
# 2. 上面返回的是wrapper函数的内存地址,所以就可以直接加()执行wrapper函数
res() # 执行的是wrapper函数
# 3. 再看wrapper函数做了什么?
# wrapper 函数运行的是func函数,而func函数就是传入的test()函数,所以执行wrapper就可以执行test()函数
# 所以,这个时候就可以添加代码来统计test函数的运行时间了。 至此,
# 上面的代码就演化成为:
def timer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print("test函数的运行时长是: %s" % (stop_time - start_time))
return wrapper def test():
time.sleep(2)
print("test函数运行完毕") res = timer(test)
res()
# 运行结果
test函数运行完毕
test函数的运行时长是: 2.000473976135254 但是,这里有 个问题,需求是不修改test函数的调研方式,test函数的调用方式应该是test(),而这里的调用方式是res() 所以,解决修改了test函数调用方式这个问题的方法:
# 将timer(test)的值赋给变量test
test = timer(test)
# 然后再test()调用,这样就达到了没有修改test函数调用方式的目的
test() #到这里,一个装饰器的架子和调用模型就初步成型了,但是还需要赋值给test变量这一步操作 # 所以,最后,
python为了实现这个不修改test函数调用方式,也省略赋值这行代码,而产生了一个装饰器的概念 # 上面的代码就进化为:
# 装饰器
def timer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print("test函数的运行时长是: %s" % (stop_time - start_time))
return wrapper @timer #在test函数的顶头加上 @timmer来修饰test函数, 就相当于 test=timmer(test) 这个赋值动作
def test():
time.sleep(2)
print("test函数运行完毕") # 有了装饰器,就可以直接调用test函数
test() # 终于啊,老天爷啊,历经千辛万苦,终于完美的满足了最开始的需求

为闭包加上返回值

还是接上面的基本装饰器来说

def timer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print("test函数的运行时长是: %s" % (stop_time - start_time))
return wrapper # 需求:给test函数加一个返回值: “test函数返回好消息了”
@timer
def test():
time.sleep(2)
print("test函数运行完毕")
return "test函数返回好消息了" res=test()
print(res)
#结果
test函数运行完毕
test函数的运行时长是: 2.000929117202759
None 返回None了 # 为什么会这样呢?分析如下:
# 1. 运行test函数,因为有装饰器,实际上运行的是wrapper这个闭包,而wrapper函数却没有返回值,所以返回的是None,那么给wrapper函数加个返回值呢?
代码如下:
def timer(func):
def wrapper():
start_time = time.time()
func()
stop_time = time.time()
print("test函数的运行时长是: %s" % (stop_time - start_time))
return 123 # 给wrapper函数加的返回值
return wrapper @timer
def test():
time.sleep(2)
print("test函数运行完毕")
return "test函数返回好消息了" res = test()
print(res)
# 结果
test函数运行完毕
test函数的运行时长是: 2.0010011196136475
123 # 返回的是123, # 上面运行返回的是123,而我们的需求是要返回“test函数返回好消息了”
# 接着开,wrapper函数运行的是func函数,而func函数就是传入的test函数,所以把 func()赋值给变量 res呢? # 代码如下:
def timer(func):
def wrapper():
start_time = time.time()
res = func() # 赋值给res
stop_time = time.time()
print("test函数的运行时长是: %s" % (stop_time - start_time))
return res # 返回res
return wrapper @timer
def test():
time.sleep(2)
print("test函数运行完毕")
return "test函数返回好消息了" res = test()
print(res) # 结果
test函数运行完毕
test函数的运行时长是: 2.0009818077087402
test函数返回好消息了 # 至此,给装饰器加上返回值就实现了。 # 但是还有个问题,如果直接调用test呢?
test()
# 结果如下,为什么?暂时还不明白
test函数运行完毕
test函数的运行时长是: 2.0001914501190186

 为闭包加上参数

# 需求:给test传入参数

import time
def timmer(func): #func=test1
def wrapper():
start_time=time.time()
res=func()
stop_time = time.time()
print('运行时间是%s' %(stop_time-start_time))
return res
return wrapper @timmer #test=timmer(test)
def test(name,age):
time.sleep(3)
print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))
return '这是test的返回值' res=test('lixiao',age=18) #就是在运行wrapper
print(res)
#结果:
报错 为什么呢?
因为运行test就是在运行wrapper,而test传入了参数,wrapper里却没有接受参数,所以就报错了。那怎么改呢?
1. 礼尚往来,你给我得接着呀,所有wrapper需要有参数接受test传来的值。
2. wrapper 接受到了传来的值,wrapper函数体内,运行的res=func()实际上就是test(),所以,wrapper接受的参数也要传给func()才行。 import time
def timmer(func): #func=test1
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time = time.time()
print('运行时间是%s' %(stop_time-start_time))
return res
return wrapper @timmer #test=timmer(test)
def test(name,age):
time.sleep(3)
print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))
return '这是test的返回值' #备注:
wrapper(*args,**kwargs):表示的是可变长参数
# *args: 代表把所有的位置参数接成列表
# **kwargs: 代表可能用户传值的时候通过关键字传值,比如用户传的值是age =18, 或者{"age": 18}形式的, 就用**kwargs来接 # 所以,*args,**kwargs :代表可变长参数,传多少参数,任意形式的都可以接受了 @timmer
def test1(name,age,gender):
time.sleep(1)
print('test1函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender))
return '这是test的返回值' test1('alex', 18, gender='male')

闭包:补充解压序列 (很有用)

# 需求:有个很长的列表,拿出第一个和最后一个值

l = [1,2,4,5,6,123,34,1,6]

方式1:索引的方式
print(l[0])
print(l[-1]) 方式2:解压序列方式 l = [1,2,4,5,6,123,34,1,6] a, *_, c = l
print(a) #
print(*_) # 2 4 5 6 123 34 1
print(c) # # a 代表取第一个值
# * 代表所有的值,下划线_表示不要了,当然下划线可以有任意字符替换, 如 *b, *d都可以
# c 代表最后一个值了 # 需求:a =1, b=2, 交换a,b的值
a = 1
b = 2 a,b = b,a
print(a) #
print(b) #

函数闭包为函数加上认证功能

def auth_func(func):
def wrapper(*args, **kwargs):
username = input('用户名: ').strip()
password = input("密码: ").strip()
if username =="sb" and password == "": # 用户是写死了
res = func(*args, **kwargs)
return res # 切记要有返回值那
else:
print("用户名或密码错误")
return wrapper @auth_func
def index():
print("欢迎来到京东") @auth_func
def home():
print("欢迎回家") @auth_func
def shopping_box(name):
print("%s的购物车里有%s,%s,%s" % (name, '娃娃', '电脑','手机')) index()
home()
shopping_box('韩梅梅')

 函数闭包模拟session

上面为每个功能都加了验证功能,但是每次运行其他功能都需要重新登陆,这显然是不合理的,可以用session和cookie来解决,那怎么模拟解决这个问题呢?

还有一个问题,就是用户是写死的,而实战中,我们的用户不可能只有一个,而且写死,所以这里也是需要改造的。

# 用户一般都是存在数据库里,这里为了方便模拟,就把用户存储在一个列表里
user_list = [
{'name': 'sb', 'passwd': ''},
{'name': 'lhf', 'passwd': ''},
{'name': 'wpq', 'passwd': ''},
{'name': 'yh', 'passwd': ''},
] current_dic = {'username':None, 'login':False} # 模式当前登录状态是FALSE,未登录 def auth_func(func):
def wrapper(*args, **kwargs):
# 在运行wrapper之前, 先判断是否有用户登录
if current_dic['username'] and current_dic['login']: # 这个代表的是由用户登录且是true,然后直接运行func()
res= func(*args, **kwargs)
return res
# 如果上面的判断不为True,则输入用户名和密码
username = input("用户名:")
password = input("密 码:")
# 输完后应该从user_list里提取信息
for user in user_list:
# 然后判断输入的用户名和密码与user_list里的是否一致
if username == user['name'] and password == user['passwd']:
# 如果有一致的, 要把当前状态改一下
current_dic['username'] = username # 记录状态的字典里用户名 = 用户输入的用户名
current_dic['login'] = True # 登陆状态改为true
res = func(*args, **kwargs)
return res # 切记要有返回值那
# 如果遍历了一遍,都没有匹配的
else:
print("用户名或密码错误")
return wrapper @auth_func
def index():
print("欢迎来到京东") @auth_func
def home():
print("欢迎回家") @auth_func
def shopping_box(name):
print("%s的购物车里有%s,%s,%s" % (name, '娃娃', '电脑','手机')) index()
home()
shopping_box('韩梅梅')

函数闭包带参数装饰器-----带参数的装饰器

user_list=[
{'name':'alex','passwd':''},
{'name':'linhaifeng','passwd':''},
{'name':'wupeiqi','passwd':''},
{'name':'yuanhao','passwd':''},
]
current_dic={'username':None,'login':False} def auth(auth_type='filedb'):
def auth_func(func):
def wrapper(*args,**kwargs):
print('认证类型是',auth_type)
if auth_type == 'filedb':
if current_dic['username'] and current_dic['login']:
res = func(*args, **kwargs)
return res
username=input('用户名:').strip()
passwd=input('密码:').strip()
for user_dic in user_list:
if username == user_dic['name'] and passwd == user_dic['passwd']:
current_dic['username']=username
current_dic['login']=True
res = func(*args, **kwargs)
return res
else:
print('用户名或者密码错误')
elif auth_type == 'ldap':
print('鬼才特么会玩')
res = func(*args, **kwargs)
return res
else:
print('鬼才知道你用的什么认证方式')
res = func(*args, **kwargs)
return res return wrapper
return auth_func @auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type --->index=auth_func(index)
def index():
print('欢迎来到京东主页') @auth(auth_type='ldap')
def home(name):
print('欢迎回家%s' %name)
#
@auth(auth_type='sssssss')
def shopping_car(name):
print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) # print('before-->',current_dic)
index()
# print('after--->',current_dic)
home('产品经理')
shopping_car('产品经理')

第十七篇 Python函数之闭包与装饰器的更多相关文章

  1. Python之路【第五篇】: 函数、闭包、装饰器、迭代器、生成器

    目录 函数补充进阶 函数对象 函数的嵌套 名称空间与作用域 闭包函数 函数之装饰器 函数之可迭代对象 函数之迭代器 函数之生成器 面向过程的程序设计思想 一.函数进阶之函数对象 1. 函数对象 秉承着 ...

  2. 一文搞懂Python函数(匿名函数、嵌套函数、闭包、装饰器)!

    Python函数定义.匿名函数.嵌套函数.闭包.装饰器 目录 Python函数定义.匿名函数.嵌套函数.闭包.装饰器 函数核心理解 1. 函数定义 2. 嵌套函数 2.1 作用 2.2 函数变量作用域 ...

  3. day20_函数的闭包 与 装饰器

    #!/usr/bin/env python # -*- coding:utf-8 -*- # # 一些文章 # https://www.cnblogs.com/Vae1242/p/6944338.ht ...

  4. Python 函数修饰符(装饰器)的使用

     Python 函数修饰符(装饰器)的使用 1.  修饰符的来源修饰符是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等. 修饰符是解决这类问题的绝佳设计, ...

  5. Python函数07/有参装饰器/多个装饰器装饰一个函数

    Python函数07/有参装饰器/多个装饰器装饰一个函数 目录 Python函数07/有参装饰器/多个装饰器装饰一个函数 内容大纲 1.有参装饰器 2.多个装饰器装饰一个函数 3.今日总结 3.今日练 ...

  6. 13、python中的函数(闭包与装饰器)

    一.嵌套函数 函数的内部又再定义另一个函数,这个函数就叫嵌套函数,里面含函数就叫内部函数. 示例: 二.返回函数 函数可以接收函数对象作为参数,同理函数也能返回一个函数对象作为返回值. 示例: 返回函 ...

  7. Python基础之函数的闭包与装饰器的介绍

    1.闭包的概念: 如果在一个函数中,定义了另外一个函数,并且那个函数使用了外面函数的变量,并且外面那个函数返回了里面这个函数的引用,那么称为里面的这个函数为闭包. 2.话不多说,以demo示例: de ...

  8. 轻松理解python中的闭包和装饰器 (下)

    在 上篇 我们讲了python将函数做为返回值和闭包的概念,下面我们继续讲解函数做参数和装饰器,这个功能相当方便实用,可以极大地简化代码,就让我们go on吧! 能接受函数做参数的函数我们称之为高阶函 ...

  9. python中的闭包和装饰器

    重新学习完了函数,是时候将其中的一些重点重新捋一捋了,本次总结的东西只有闭包和装饰器 1.闭包 闭包是python函数中的一个比较重要功能,一般闭包都是用在装饰器上,一般学完闭包就会去学习装饰器,这俩 ...

随机推荐

  1. 【转】Java虚拟机类型卸载和类型更新解析

    [摘要]         前面系统讨论过java类型加载(loading)的问题,在这篇文章中简要分析一下java类型卸载(unloading)的问题,并简要分析一下如何解决如何运行时加载newly ...

  2. 九、IntelliJ IDEA 编译方式介绍及编译器的设置和选择

    相对于 Eclipse 的实时自动编译,IntelliJ IDEA 的编译更加手动化,虽然 IntelliJ IDEA 也可以通过设置开启实时编译,但是太浪费资源了,因此不建议这样做.IntelliJ ...

  3. 掘金上发现的有趣web api

    本篇文章主要选取了几个有趣且有用的webapi进行介绍,分别介绍其用法.用处以及浏览器支持度 page lifecycle onlineState(网络状态) device orientation(陀 ...

  4. C++的抽象类、虚函数、虚基类和java的抽象类和接口

    简单整理如下: C++虚函数 == java普通函数 C++纯虚函数 == java抽象函数 C++抽象类 == java抽象类 C++虚基类(全都是纯虚函数) == java接口

  5. 带你解析Java类加载机制

      目录 Java类加载机制的七个阶段 加载 验证 准备(重点) 解析 初始化(重点) 使用 卸载 实战分析 方法论 树义有话说 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如 ...

  6. cc++面试------17道经典面试题目分析

    以下是C/C++面试题目,共计17个题目,其中涵盖了c的各种基础语法和算法, 以函数接口设计和算法设计为主.这17个题目在C/C++面试方面已经流行了多 年,大家需要抽时间掌握好,每一个题目后面附有参 ...

  7. SP1716 GSS3 - Can you answer these queries III(单点修改,区间最大子段和)

    题意翻译 nnn 个数, qqq 次操作 操作0 x y把 AxA_xAx​ 修改为 yyy 操作1 l r询问区间 [l,r][l, r][l,r] 的最大子段和 题目描述 You are give ...

  8. POJ 1180 Batch Scheduling (dp,双端队列)

    #include <iostream> using namespace std; + ; int S, N; int T[MAX_N], F[MAX_N]; int sum_F[MAX_N ...

  9. WebMagic 启动例子报错

    报错内容: Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/conn/Http ...

  10. 转:Java子线程中的异常处理(通用)

    引自:https://www.cnblogs.com/yangfanexp/p/7594557.html 在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally . ...