闭包函数

什么是闭包函数

闭包函数本质上就是函数嵌套和高阶函数

闭包函数的满足条件:

  • 必须嵌套函数
  • 内嵌函数必须引用外部函数的变量
  • 外部函数必须返回内嵌函数的函数对象(函数名)
# outer 是一个闭包函数
def outer():
x = 1
def inner():
print(X) # 内嵌函数引用了外部函数里的变量
return inner # 外部函数返回内嵌函数的函数对象 f1 = outer()
f1()

闭包函数的作用

闭包函数不仅返回了内嵌函数的函数对象, 还在其身上包裹了一层(外部函数)的局部作用域. 这使得, 无论该函数对象在何处被调用, 都优先使用包裹在自己身上的这层作用域.

# 常规函数
import requests def get(url):
response = requests.get(url)
print(f'done:{url}) # 如果后续要多次调用get函数爬取同一网站, 则每次都要输入一次网址
get('https://www.python.org')
get('https://www.python.org')
get('https://www.python.org') # 使用默认形参也只能解决一个网站
get('https://www.cnblogs.com/bigb/')
get('https://www.cnblogs.com/bigb/')
get('https://www.cnblogs.com/bigb/')

我们可以用闭包函数解决上述问题

# 闭包函数
import requests # url是外部函数outer的形参, 可以把其看成函数outer局部作用域中的变量
def outer(url):
def get():
response = requests.get(url)
print(f'done:{}')
return get # 在外部函数空间作用域中存在 url = 'https://www.python.org'这个变量, 内部函数可以永久引用
python = outer('https://www.python.org') python() # get()
python()
python()

装饰器

什么是装饰器

装饰器本质上就是一个闭包函数, 为被装饰对象添加额外功能

装饰器的实现必须遵循两大原则:

  • 不修改被装饰对象源代码
  • 不修改被装饰对象的调用方式

无参装饰器

下面我们就用装饰器给既定函数f1增加计时功能

import time

def f1():
'''被装饰函数'''
print('this is f1')
time.sleep(1) # 程序休眠1秒 # 定义time_count装饰器函数, func是形参名, 用来接收函数对象
def time_count(func):
'''装饰器函数''' # wrapper是辅助函数
def wrapper():
start = time.time()
func() # 使用函数对象调用函数
end = time.time()
print(f'程序耗费时长为: {end - start}') return wrapper f1 = time_count(f1) # wrapper
f1() # wrapper() '''
this is f1
程序耗费时长为: 1.0000574588775635
'''

如果f1是个有参函数我们该怎样通过装饰器传入参数呢?

由上述例子我们知道, f1() = wrapper(), 因此我们给wrapper传参即可

import time

def f1(a):
'''被装饰函数'''
print('this is f1, 参数:', a)
time.sleep(1) # 程序休眠1秒 # 定义time_count装饰器函数, func是形参名, 用来接收函数对象
def time_count(func):
'''装饰器函数''' # wrapper是辅助函数
def wrapper(*args,**kwargs):
start = time.time()
func(*args,**kwargs) # 使用函数对象调用函数
end = time.time()
print(f'程序耗费时长为: {end - start}') return wrapper f1 = time_count(f1) # wrapper
f1(1) # wrapper() '''
this is f1, 参数: 1
程序耗费时长为: 1.0000572204589844
'''

如果函数f1有返回值我们如何通过装饰器实现呢?

由于f1() = wrapper(), 因此f1 的返回值就是wrapper的返回值

import time

def f1(a):
'''被装饰函数'''
print('this is f1, 参数:', a)
time.sleep(1) # 程序休眠1秒
return a # 定义time_count装饰器函数, func是形参名, 用来接收函数对象
def time_count(func):
'''装饰器函数''' # wrapper是辅助函数
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs) # 使用函数对象调用函数
end = time.time()
print(f'程序耗费时长为: {end - start}')
return res return wrapper f1 = time_count(f1) # wrapper
res = f1(1)
print(res) '''
this is f1, 参数: 1
程序耗费时长为: 1.0000569820404053
1
'''

还是不理解装饰器吗? 那就记住装饰器的模板吧!

def deco(func):
def wrapper(*args,**kwargs):
# 添加功能
res = func(*args, **kwargs)
# 添加功能
return res
return wrapper

装饰器语法糖:

就是在被装饰函数上面单独写上@装饰器名 , 它实现的功能是f1 = time_count(f1)

@time_count
def f1(a):
'''被装饰函数'''
print('this is f1, 参数:', a)
time.sleep(1) # 程序休眠1秒
return a res = f1(1)
print(res) '''
this is f1, 参数: 1
程序耗费时长为: 1.0000569820404053
1
'''

有参装饰器

我们现在写一个登陆功能的装饰器

# 登陆装饰器

username_list = []

def login(func):
def wrapper(*args, **kwargs): if username_list:
print('请勿重复登陆')
res = func(*args, **kwargs)
return res username_inp = input('请输入用户名: ')
pwd_inp = input('请输入密码: ') # 从user_info读取用户信息
with open('user_info', 'r', encoding='utf-8') as fr:
for user_info in fr:
name, pwd = user_info.strip().split(':') if name == username_inp and pwd == pwd_inp:
print('登录成功')
username_list.append(username_inp) res = func(*args, **kwargs) # 先调用 再返回
return res
else:
print('用户名密码错误') res = func(*args, **kwargs) return res return wrapper

上面我只实现了从user_info里面读取用户密码信息进行判断.

现在我们想实现管理员登陆则从admin_info里面读取信息, 用户登陆则从user_info里面读取信息, 该怎么办呢?

username_list = []

def sanceng(role):

    def login(func):

        def wrapper(*args, **kwargs):

            if username_list:
print('请勿重复登陆')
res = func(*args, **kwargs)
return res username_inp = input('请输入用户名: ')
pwd_inp = input('请输入密码: ') # 从{role}_info读取用户信息
with open(f'{role}_info', 'r', encoding='utf-8') as fr:
for user_info in fr:
name, pwd = user_info.strip().split(':') if name == username_inp and pwd == pwd_inp:
print('登录成功')
username_list.append(username_inp) res = func(*args, **kwargs) # 先调用 再返回
return res
else:
print('用户名密码错误') return wrapper return login # 那我们在装饰 被装饰对象 时就要输入一个参数
@sanceng(admin)
def f1():
pass

Python3 函数进阶1的更多相关文章

  1. 5 Python3 函数进阶&迭代器与生成器

    1.函数进阶 1.1.名称空间 又名name space, 顾名思义就是存放名字的地方,存什么名字呢?举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的 ...

  2. Python3 函数进阶3

    目录 匿名函数 定义匿名函数 匿名函数的使用 内置函数 匿名函数 定义匿名函数 我们之前定义的函数都是有名函数, 我们可以通过函数名来调用 匿名函数顾名思义就是一种没有绑定函数名的函数, 使用一次既被 ...

  3. Python3 函数进阶2

    目录 迭代器 可迭代对象 迭代器对象 总结和补充 列表推导式 字典生成式 zip()函数 递归 迭代器 迭代器是访问容器类数据类型元素的一种方式 迭代器是一个可以记住遍历的位置的对象 迭代器对象从容器 ...

  4. python3函数进阶

    1.命名空间和作用域 命名空间     加载         内置命名空间             python解释器自带的变量和函数             开启python解释器自动加载内置命名空 ...

  5. 洗礼灵魂,修炼python(25)--自定义函数(6)—从匿名函数进阶话题讲解中解析“函数式编程”

    匿名函数进阶 前一章已经说了匿名函数,匿名函数还可以和其他内置函数结合使用 1.map map():映射器,映射 list(map(lambda x:x*2,range(10))) #把range产生 ...

  6. 10.Python初窥门径(函数进阶)

    Python(函数进阶) 一.函数的传参(接上期) 形参角度(一共四种,后两种) 动态参数(万能参数)* # 定义一个函数时,*所有的位置参数聚合到一个元组中 def func(*args): # * ...

  7. 全面系统Python3入门+进阶课程 ✌✌

    全面系统Python3入门+进阶课程 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 无论是大数据.人工智能还是机器学习,Python都是最热门的首选语言 ,这 ...

  8. 深入理解javascript函数进阶系列第一篇——高阶函数

    前面的话 前面的函数系列中介绍了函数的基础用法.从本文开始,将介绍javascript函数进阶系列,本文将详细介绍高阶函数 定义 高阶函数(higher-order function)指操作函数的函数 ...

  9. python3函数

    一.python3函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.可以自己创建函数,这被叫做用户自定义函数. 1.定义函数规则 函 ...

随机推荐

  1. 前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?

    前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的情况,大都是一两句话就把问题抛出来了,啥 ...

  2. mongodb基本命令,mongodb集群原理分析

    mongodb基本命令,mongodb集群原理分析 集合: 1.集合没有固定数据格式. 2. 数据: 时间类型: Date() 当前时间(js时间) new Date() 格林尼治时间(object) ...

  3. iOS核心动画高级技巧-2

    3. 图层几何学 图层几何学 不熟悉几何学的人就不要来这里了 --柏拉图学院入口的签名 在第二章里面,我们介绍了图层背后的图片,和一些控制图层坐标和旋转的属性.在这一章中,我们将要看一看图层内部是如何 ...

  4. 【MongoDB】2019年MongoDB中文社区广州大会,干货满满的分享活动

    1 介绍 MongoDB中文社区(mongoing.com)是大中华区获得官方认可的中文社区,11月23日下午,在广州举办了线下用户大会,带给大家一手干货和实践. 2 大会议程 大会组织者对时间的把控 ...

  5. js对象的sessionStorage,判断对象相等,判断是否包含某属性

    一,storage storage只能存储字符串的数据,对于JS中常用的数组或对象却不能直接存储 因此需要借JSON进行类型转化来存储: let obj = { name:'Jim' } sessio ...

  6. 如何在C++中使用boost库序列化自定义class ?| serialize and deserialize a class in cpp with boost

    本文首发于个人博客https://kezunlin.me/post/6887a6ee/,欢迎阅读! serialize and deserialize a class in cpp Guide how ...

  7. python3 之 函数传参

    一.可变对象与不可变对象 在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象. 不可变类型:变量赋值 a=5 后 ...

  8. urllib练习

    # coding = utf-8 """ 解析https://www.kuaidaili.com/free/网站里的代理地址, 并测试是否可用 ""& ...

  9. 【IntelliJ IDEA】IDEA自动生成serialVersionUID的办法

    digest:实体对象实现了java.io.Serializable接口后,一般都会提供一个serialVersionUID一做版本区分.在IDEA里,可以通过一些设置,帮助我们快速生成serialV ...

  10. easywechat微信开发SDK之小微商户进件(一)

    微信本身不提供小微商户进件的SDK,偶然发现easywechat这么个东西,官网地址是https://www.easywechat.com/  整合了微信开发中常用的接口,包括微信公众号相关接口,微信 ...