闭包函数

什么是闭包函数

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

闭包函数的满足条件:

  • 必须嵌套函数
  • 内嵌函数必须引用外部函数的变量
  • 外部函数必须返回内嵌函数的函数对象(函数名)
# 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. 力扣(LeetCode)翻转字符串里的单词 个人题解

    给定一个字符串,逐个翻转字符串中的每个单词. 示例 1: 输入: "the sky is blue" 输出: "blue is sky the" 示例 2: 输 ...

  2. 阿里云ECS服务器部署HADOOP集群(二):HBase完全分布式集群搭建(使用外置ZooKeeper)

    本篇将在阿里云ECS服务器部署HADOOP集群(一):Hadoop完全分布式集群环境搭建的基础上搭建,多添加了一个 datanode 节点 . 1 节点环境介绍: 1.1 环境介绍: 服务器:三台阿里 ...

  3. Java-selenium

    public class chrometest { public static void main(String[] args) throws InterruptedException { Strin ...

  4. 基于Vue的日历组件

    可以标注重要日子 自己写的,可能不是特别很好,大家多提意见!!! 地址:https://github.com/jsLWQ/calendar

  5. Linux菜鸟——常见命令一 查看及创建目录文件等命令

    命令行提示符 [root@localhost ~]# [当前用户名@主机名 当前所在目录]$ linux 超级用户 root window 超级用户 administartor # 超级用户 $ 普通 ...

  6. 检测当前IE浏览器的版本

    检测当前IE浏览器的版本(注意:在非IE浏览器中是看不到效果的) 使用示例如下:低于IE8弹窗提示 <!--[if lte IE 8]><script>alert('您当前浏览 ...

  7. LXC容器文件系统设计优化

    在HOST上面,一个LXC container包含一个config文件和一个rootfs目录. 早期我们在交叉编译系统上编译出container的rootfs之后,直接在编译系统上将其用tar压缩打包 ...

  8. GeoServer 查询sql视图

    说明: 最近项目中遇到一个需求,需要统计管网的长度,但管网数据量非常大,前端用openlayers接口统计直接就奔溃了. 后尝试使用调后台接口查数据库的方式,虽然可行但是又要多一层与后台交互的工作. ...

  9. 扛把子组2018092609-2 选题 Scrum立会报告+燃尽图 04

    此作业的要求参见[https://edu.cnblogs.com/campus/nenu/2019fall/homework/8682] 一.小组情况组长:迟俊文组员:宋晓丽 梁梦瑶 韩昊 刘信鹏队名 ...

  10. pymongo的基本操作和使用--练习

    1.将MongoDB注册到电脑中 安装好MongoDB之后,如何使用MongoDB呢?来到安装目录D:/MongoDB/bin会有如下列表: 其中,mongod.exe是服务端,mongo.exe是客 ...