闭包函数

闭包概念

  • 闭:定义在函数内部的函数
  • 包:内部函数使用了外层函数名称空间中的名字
# 闭包函数
def outer():
x = 111
# 定义在函数内部的函数
def inner():
# 使用了外层函数名称空间中的名字
print(x)
return inner
x = 666
res = outer()
res() # 输出:111

实际应用

闭包函数是给函数体传参的另外一种方式

def outer(username):
def index():
print(username)
return index
res = outer('kevin') # 形参username与值kevin临时绑定
res() # 输出:kevin
res1 = outer('jason') # 形参username与值jason临时绑定
res1() # 输出:jason

这时候有人就很迷惑了,明明只要定义一个函数index()就可以了,为什么要用outer()包起来传参呢?别急,这是为接下来的内容做铺垫用的。

装饰器

简介

装饰器并不是一个新的知识点,而是由前面所有的函数知识点整合到一起的产物。

装饰器的本质:在不改变被装饰对象原有的“调用方式”和“内部代码”的情况下给被装饰对象添加新的功能。

这是不是听起来有些神奇?装饰器的原则就是对扩展开放,对修改封闭。

简单版本装饰器

了解完什么是装饰器之后,我们先学习使用简单版本的装饰器。

现在有一段代码:

import time  # 导入模块,不用管什么意思
def index():
time.sleep(1) # 让程序休眠1秒钟
print('这里是index函数') def home():
time.sleep(1) # 让程序休眠1秒钟
print('这里是home函数')

我如何能在不动上面2个函数的情况下,添加一个功能:输出函数运行的时长。

这时候有人就要说了:啊?这不是很简单,我只要添加一个函数

def get_time(function_name):
start_time = time.time() # 获取运行函数前的时间
function_name() # 调用函数
end_time = time.time() # 获取运行结束后的时间
print(end_time - start_time) # 输出时间差 get_time(index)
get_time(home)

这个方法确实是可以,但是运行函数都要调用get_time()。

所以别着急,装饰器的功能还没了解全之前,请先继续看下去。

实现:

import time  # 导入模块,不用管什么意思

# 装饰对象
def index():
time.sleep(1) # 让程序休眠1秒钟
print('这里是index函数') def home():
time.sleep(1) # 让程序休眠1秒钟
print('这里是home函数') # 装饰器
def outer(function_name):
def inner():
# 获取运行函数前的时间
start_time = time.time()
# 调用名为function_name函数
function_name()
# 获取函数运行结束后的时间
end_time = time.time()
# 输出时间差
print(end_time - start_time)
# 返回内部函数名
return inner
# 让index()变成inner()
index = outer(index)
index()
"""
输出:
这里是index函数
1.0101792812347412
""" # 狸猫换太子
home = outer(home)
home()
"""
输出:
这里是home函数
1.0047881603240967
"""

这个时候函数只需要调用它自己就能输出运行时间了,相当于给函数内部添加了一段代码一样。

进阶版本装饰器

这时候如果函数有参数该怎么做呢?并且参数还不知道几个?

很简单,只要加上可变长形参就行了。

实现:

import time

def index(a):  # 有参数
time.sleep(1)
print('这里是index函数', a) def home(): # 无参数
time.sleep(1)
print('这里是home函数') def outer(function_name):
def inner(*args, **kwargs): # 添加可变长形参
start_time = time.time()
function_name(*args, **kwargs) # 传参
end_time = time.time()
print(end_time - start_time)
return inner
index = outer(index)
index(555)
"""
输出:
这里是index函数 555
1.0047197341918945
""" home = outer(home)
home()
"""
输出:
这里是home函数
1.0003490447998047
"""

完整版本装饰器

这个时候问题又出现了,如果函数有返回值又该怎么办?

也很简单,在装饰器内部函数加一个return返回值就行了。

实现:

import time

def index(a):
time.sleep(1)
print('这里是index函数')
return a # 有返回值 def home():
time.sleep(1)
print('这里是home函数') def outer(function_name):
def inner(*args, **kwargs):
start_time = time.time()
# 将函数返回值赋值给res
res = function_name(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
"""返回函数的返回值"""
return res
return inner
index = outer(index)
print(index(555))
"""
输出:
这里是index函数
1.0073633193969727
555
""" home = outer(home)
home()
"""
输出:
这里是home函数
1.0051474571228027
"""

装饰器模板

还没搞明白装饰器怎么回事?小问题!这里有一套万能模板,只要知道怎么使用装饰器就行了。

'''编写装饰器其实有一套固定的代码 不需要做任何理解'''
def outer(func_name): # func_name用于接收被装饰的对象(函数)
def inner(*args, **kwargs):
print('执行被装饰函数之前 可以做的额外操作')
res = func_name(*args, **kwargs) # 执行真正的被装饰函数
print('执行被装饰函数之后 可以做的额外操作')
return res # 返回真正函数的返回值
return inner

装饰器语法糖

由于感觉在这块代码太繁琐了

index = outer(index)
index()

于是python给了一个方法,用@+装饰器名称放在装饰对象的上方。

实现:

def outer(func_name):
def inner(*args, **kwargs):
# 额外操作
res = func_name(*args, **kwargs)
# 额外操作
return res
return inner # 装饰器语法糖
@outer
def index():
print('这里是index函数') """可以直接使用index(),省去了一个步骤"""
index()

语法糖内部原理:

  1. 使用的时候最好紧跟在被装饰对象的上方
  2. 语法糖会自动将下面紧挨着的函数名传给@后面的函数调用

装饰器修复技术

在了解装饰器修复技术之前,我们先了解被装饰对象的内部属于谁了

def outer(func_name):
def inner(*args, **kwargs):
res = func_name(*args, **kwargs)
return res
return inner @outer
def index():
print('这里是index函数') print(index)
"""
输出:
<function outer.<locals>.inner at 0x0000025BA5B9F268>
"""

可以看出,这时的index已经是inner的人了,这就让人很不爽了,所以我们要把它恢复回来。

from functools import wraps  # 导入模块
def outer(func_name):
@wraps(func_name) # 修复
def inner(*args, **kwargs):
res = func_name(*args, **kwargs)
return res
return inner @outer
def index():
print('这里是index函数') print(index)
"""
输出:
<function index at 0x00000133924FF268>
"""

可以看到,index还是原来的index。

问题

编写一个用户认证装饰器:

  • 基本要求

    执行每个函数的时候必须先校验身份,eg: jason 123

  • 拔高练习

    执行被装饰的函数,只要有一次认证成功,那么后续的校验都通过

    提示:全局变量 记录当前用户是否认证

答案

点击查看代码(这里只展示简易版本)
# 定义全局变量,用于判断用户是否成功登录过一次
is_login = False # 编辑装饰器
def login(function_name): def for_login(*args, **kwargs):
global is_login # 声明is_login使用的是全局变量 # 如果is_login为True就不需要登录了
if is_login:
res = function_name(*args, **kwargs)
return res # 用户没有登录过的情况
print("你还没有登录哦!")
username = input("请输入用户名:").strip()
password = input("请输入密码:").strip()
if username == 'yume' and password == '123':
print("登录成功!")
is_login = True
res = function_name(*args, **kwargs)
return res
else:
print("用户名或密码错误!") return for_login @login
def print_any():
print("输入什么就输出什么")
s = input()
print(s) @login
def add():
print("将2个字符串相加")
a = input("输入第一个:")
b = input("输入第二个:")
print(a + b) print("""
1.输入什么就输出什么
2.将2个字符串相加
""")
while True:
choice = input("请输入指令:")
if choice == '1':
print_any()
elif choice == '2':
add()
else:
print("错误指令")

python闭包函数与装饰器的更多相关文章

  1. python 闭包函数与装饰器

    1.什么是闭包函数 (1):什么是闭包函数: #内部函数包含对外部作用域而非全局作用域的引用, 简而言之, 闭包的特点就是内部函数引用了外部函数中的变量. 在Python中,支持将函数当做对象使用,也 ...

  2. python闭包函数、装饰器

    闭包函数的传值方式: 方式1:通过参数传值 def func(x): print(x)func(1) 方式2:闭包函数传值 def outter(x): def inner(): print(x) r ...

  3. python闭包函数及装饰器简介

    目录: 闭包函数简介 闭包函数的实际应用 装饰器简介 装饰器初期-完整版 装饰器语法糖 闭包函数简介 1.定义在函数内部的函数(函数的嵌套) 2.内部函数运用外部函数局部名称空间中的变量名 注:函数名 ...

  4. python基础-闭包函数和装饰器

    闭包函数和装饰器 闭包函数 概念:在函数中(嵌套)定义另一个函数时,内部函数引用了外层函数的名字. 特性 闭包函数必须在函数内部定义 闭包函数可引用外层函数的名字 闭包函数是函数嵌套.函数对象.名称空 ...

  5. day11 闭包函数和装饰器

    1.函数不是新知识点 主要是学习函数的用法 在面向对象编程中,一切皆对象,函数也不例外 具体的表现 1.函数可以引用 2.函数可以作为函数的参数 3.函数可以作为函数的返回值 4.可以被存储到容器类型 ...

  6. Python之函数对象、函数嵌套、名称空间与作用域、闭包函数、装饰器

    目录 一 函数对象 二 函数的嵌套 三 名称空间和作用域 四 闭合函数 五 装饰器 一.函数对象 1.函数是第一类对象 #第一类对象的特性:# 可以被引用 # 可以当做参数传递 # 返回值是函数 # ...

  7. 【Python3的命名空间与作用域,闭包函数,装饰器】

    一.命名空间与作用域 命名空间是名字和对象的映射,就像是字典,key是变量名,value是变量的值 1.命名空间的定义 name='egon' #定义变量 def func(): #定义函数 pass ...

  8. day11_7.11 闭包函数与装饰器

    补充: callable 代表可调用的,加括号可以执行.(函数或者类) import this  查看python之禅 一.闭包函数 所谓闭包函数,就是定义在函数内部的函数,也就是函数定义的嵌套.而在 ...

  9. CSIC_716_20191112【闭包函数和装饰器】

    闭包函数 什么是闭包函数:闭包函数是函数嵌套.函数对象.名称空间和作用域的集合体. 闭包函数必须在函数内部定义,闭包函数可以引用外层函数的名字. # _*_ coding: gbk _*_ # @Au ...

随机推荐

  1. css样式权重优先级,css样式优先级

    原文:http://www.bkjia.com/Javascri... 样式选择器权重优先级: important > 内嵌样式 > ID > 类 > 标签 | 伪类 | 属性 ...

  2. html5网页录音和语音识别

    背景 在输入方式上,人们总是在追寻一种更高效,门槛更低的方式,来降低用户使用产品的学习成本.语音输入也是一种尝试较多的方式,有些直接使用语音(如微信语音聊天),有些需要将语音转化为文字(语音识别).接 ...

  3. 项目需求与分析--NABCD模型

    合作项目特点NABCD分析结果: 特点:便捷 N(Need 需求):在大学期间内,我们通常会有许多不用的课本或书籍或者其他东西,堆积起来又没有地方放,想卖出去就要建一个群,十分麻烦,开发该软件用户可直 ...

  4. android的布局xml文件如何添加注释?

    xml布局文件如图添加注释后报错,错误内容如下: 上网查阅xml添加注释的语法规则: XML 中的注释 在 XML 中编写注释的语法与 HTML 的语法很相似: <!--This is a co ...

  5. axios路径变量传到后端没有被解析的问题

    目录就这一个(/-/) 这是一个小小的坑,大家注意一下就好,先上代码 //监听用户状态 async userStateChange(userInfo) { console.log(userInfo); ...

  6. 面试官:说一说Zookeeper中Leader选举机制

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 今天又是一个阳光明媚的一天,我又 ...

  7. 几种比较经典的波形及其FFT变换(正弦波,三角波,方波和锯齿波)

    之前上学时我的信号学得最差了,主要原因还是我高数学得不怎么样.可能是人总敬畏自己最不会的,所以我觉得我学过诸多科目中,数学是最博大精深而最妙的,从最开始的一次函数到反比例函数,二次三次函数和双曲线,椭 ...

  8. JavaWeb入门day9-随笔(session/cookie)

    会话 一个网站怎么证明用户访问过 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以:cookie 服务器登记,下次访问匹配:session 保存会话的技术 cookie 客户端技术(响应.请 ...

  9. ArcGIS使用技巧(五)——批量裁剪

    新手,若有错误还请指正! 最近用到了,所以记下来,用同一矢量范围裁剪多幅栅格数据.用到了ArcGIS中的迭代模型(图1): 图 1 首先,需要做一个准备工作,就是把需要裁剪的栅格数据放在同一数据库中( ...

  10. 2021.12.02 P4001 [ICPC-Beijing 2006]狼抓兔子(最小割)

    2021.12.02 P4001 [ICPC-Beijing 2006]狼抓兔子(最小割) https://www.luogu.com.cn/problem/P4001 题意: 把图分成两部分需要的最 ...