闭包函数

什么是闭包函数

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

闭包函数的满足条件:

  • 必须嵌套函数
  • 内嵌函数必须引用外部函数的变量
  • 外部函数必须返回内嵌函数的函数对象(函数名)
# 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. PHP 高级面试115题汇总(含答案)

    1.给你四个坐标点,判断它们能不能组成一个矩形,如判断 ([0,0],[0,1],[1,1],[1,0]) 能组成一个矩形.勾股定理,矩形是对角线相等的四边形.只要任意三点不在一条直线上,任选一点,求 ...

  2. 软件测试的原则,软件测试计划:5W1H

    1.测试应该尽早介入.        2.所有的测试都应追溯到用户需求.        3.程序员应该避免检查自己的程序.除了单元测试.因为程序员对于自己的作品,思维具有局限性.无法保证测试质量.交给 ...

  3. node读取excel文件生成JSON

    当前的目录结构 excel的数据如下: node识别excel,先得安装  node-xlsx,用npm或yarn都可以 npm install  node-xlsx 或 yarn add node- ...

  4. rhel-6.3-i386安装samba

    1.安装samba 1).安装软件 rpm –ivh /mnt/Packages/samba-3.5.10-125.el6.i686.rpm 2).创建用户 useradd myadmin 3).设置 ...

  5. (四十四)golang--协程(goroutine)和管道(channel)相结合实例

    统计1-8000之间的素数. 整体框架: 说明:有五个协程,三个管道.其中一个协程用于写入数字到intChan管道中,另外四个用于取出intChan管道中的数字并判断是否是素数,然后将素数写入到pri ...

  6. linux awk(gawk)

    awk的前世今生: awk名字的由来:分别取三个创始人Ah,Weiberger,Kernighan三个人的首字母. awk是一个报告生成器可以格式化输出文本内容.模式扫描和处理语言(pattern s ...

  7. .NET Core 跨平台 GUI 开发之 GTtkSharp 初级篇

    .NET Core 跨平台 GUI 开发之 GTtkSharp 初级篇 本文作为初级篇,适合已经安装好.NET Core 环境以及 Gtk 环境,并具备了 C#开发基础知识,能跑一些简单的例子,希望更 ...

  8. Ansible Playbooks 介绍 和 使用 一

    目录 Ansible Playbooks Playbooks 组成部分: YAML 介绍 YAML 语法 Ansible 基础元素 变量 facts registre 通过命令传递变量 通过roles ...

  9. Docker 更换国内的Hub源

    前言 通常情况下,安装的Docker默认使用的是国外的Hub源,在pull镜像的时候很慢,甚至超时了,不动了,很烦人. 更换阿里云Docker的Hub源 阿里云 - 容器Hub服务控制台:https: ...

  10. Java之Retry重试机制详解

    应用中需要实现一个功能: 需要将数据上传到远程存储服务,同时在返回处理成功情况下做其他操作.这个功能不复杂,分为两个步骤:第一步调用远程的Rest服务上传数据后对返回的结果进行处理:第二步拿到第一步结 ...