Python基础(十三)
今日主要内容
- 闭包
- 装饰器初识
- 标准装饰器
一、闭包
(一)什么是闭包
闭包:内层函数调用外层函数的变量就是闭包(不能是全局变量)
def func1():
a = 10
def func2():
print(a) # 内层函数调用外层函数的变量,这就是一个闭包 func1()
检测闭包的方法
函数名.__closure__- 若返回对象地址就是一个闭包,返回None就不是一个闭包
- 注意:
.__closure__检测的是函数名,判断这个函数是否是闭包
def func1():
a = 10
def func2():
print(a) # 调用外层变量a,是闭包
print(func2.__closure__) # 判断func2是否是闭包 func1() 运行结果:
(<cell at 0x00000230F21874F8: int object at 0x0000000063258190>,)
def func1():
a = 10
def func2():
a = 10
print(a) # 使用自身函数变量a,不是闭包
print(func2.__closure__) func1() 运行结果:
None
如何在全局空间中调用内部函数
- 外层函数返回内层函数的函数名(内存地址),就可在全局空间中调用内部函数
def func1():
a = 10
def func2():
print(a)
return func2 # 返回内层函数的函数名 f = func1() # 此时f就是func2
f() # 调用内层函数func2
print(f.__closure__) 运行结果:
10
(<cell at 0x00000280A87574F8: int object at 0x0000000063258190>,)
(二)闭包的作用
- 保护数据安全
- 说白话一点,如果数据放在全局变量中(顶格写代码),数据的安全性很低,因为所有人都可以无意间修改它,想解决这个问题我们可以把数据放到函数中,只要不调用这个函数,我的数据就是安全的。但是有个问题,每次执行函数完,解释器就会自动清空此函数开辟的局部空间中所有内容(包括数据开辟的空间),所以每次调用完函数,我的数据就没了,此时就需要利用到了闭包。闭包可以在内层函数调用外层函数中的变量,而且内层函数如果调用了外层函数中的变量,那这个变量将不会消亡,将会常驻内存。所以,我们只需要在全局空间中调用这个闭包的内层函数,就可以使用我的数据了,而且数据的安全性也提高了。
将变量常驻内存,供后续代码使用
def outer():
lst = [1,2,3,4,5]
def inner():
return lst
return inner f = outer()
f_lst = f()
print(f_lst) 运行结果:
[1,2,3,4,5]
(三)闭包的应用
- 防止数据被误修改
- 装饰器(与闭包格式相同)
二、装饰器初识
(一)软件开发的六大原则(了解)
- 开闭原则(Open Close Principle)——装饰器依据
- 里氏代换原则(Liskov Substitution Principle)
- 依赖倒转原则(Dependence Inversion Principle)
- 接口隔离原则(Interface Segregation Principle)
- 迪米特法则(最少知道原则)(Demeter Principle)
- 合成复用原则(Composite Reuse Principle)
(二)装饰器依据——开闭原则
- 开放封闭原则:
- 对功能扩展开放
- 对源码修改封闭
(三)装饰器引入
相信大多数人都玩LOL,我们模拟一次游戏过程:
- 向平常一样的流程,十连跪...
def play_lol():
print("登陆游戏")
print("开始排位...")
print("游戏中...")
print("失败...")
print("结束游戏") play_lol() 运行结果:
登陆游戏
开始排位...
游戏中...
Virtory
结束游戏
- 受不了了,开一个外挂吧,此时我的函数需要扩展,在前后添加开启关闭外挂的功能
def play_lol():
print("开启外挂!") # 添加开启外挂功能
print("登陆游戏")
print("开始排位...")
print("游戏中...")
print("胜利!!!")
print("结束游戏")
print("关闭外挂!") # 添加关闭外挂功能 play_lol() 运行结果:
开启外挂!
登陆游戏
开始排位...
游戏中...
胜利!!!
结束游戏
关闭外挂!
- 但此时,违背了开闭原则,对源代码进行了修改。想一个方法,对源代码进行前后包装,不改变源码
def play_lol():
print("登陆游戏")
print("开始排位...")
print("游戏中...")
print("胜利!!!")
print("结束游戏") def new_play(): # 将上面代码前后包装成了一个新的函数,没有改变源码
print("开启外挂!")
play_lol()
print("关闭外挂!") new_play() 运行结果:
开启外挂!
登陆游戏
开始排位...
游戏中...
胜利!!!
结束游戏
关闭外挂!
- 功能实现了,而且还没有改变源码,但是有一个问题,我们之前访问调用的是
play_lol这个函数,但此时我们访问调用的是new_play()这个函数,相当于改变了调用,还是违背了开闭原则,没有达到扩展的效果,此时我们就需要对这段代码稍作变化
def play_lol():
print("登陆游戏")
print("开始排位...")
print("游戏中...")
print("胜利!!!")
print("结束游戏") def wrapper(fn): # 装饰器雏形
def inner():
print("开启外挂!")
fn()
print("关闭外挂!")
return inner func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装
play_lol = func # 返回的是包装函数,将包装函数重命名成我原来的函数
play_lol() # 调用此函数 运行结果:
开启外挂!
登陆游戏
开始排位...
游戏中...
胜利!!!
结束游戏
关闭外挂!
- 上述代码就引出了装饰器的雏形,刨析一下:
- 装饰器雏形:与闭包的格式相同,两层函数构成:
- 内层函数就是我的包装函数,将扩展的功能和原函数包在一起组成一个函数
- 外层函数的作用就是给内层函数传参用的,传入的是我原函数的函数名,在内层调用
- 装饰器的返回值
return inner:装饰器的返回值是内层函数的函数名,真正进行包装扩展的是内层函数 - 将返回值重命名成原函数名:将返回值重命名成原来的函数名,其实就是把真正作用的包装函数重命名成原来的函数名,所以就解决了调用新函数的问题,真正遵循了开闭原则,再次调用原来的函数其实真正运行的是装饰器内部的inner函数
- 装饰器雏形:与闭包的格式相同,两层函数构成:
def wrapper(fn): # 装饰器雏形
def inner():
print("开启外挂!")
fn()
print("关闭外挂!")
return inner func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装
play_lol = func # 返回的是包装函数,将包赚函数重命名成我原来的函数
play_lol() # 调用此函数
利用语法糖装饰
- 使用装饰器的两行代码可以转换成语法糖
func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装
play_lol = func # 返回的是包装函数,将包赚函数重命名成我原来的函数
- 语法糖
@wrapper # 语法糖
def play_lol():
print("登陆游戏")
print("开始排位...")
print("游戏中...")
print("胜利!!!")
print("结束游戏")
此时,装饰器的雏形就出来了
装饰器雏形
def wrapper(fn):
def inner():
"""扩展功能"""
fn()
"""扩展功能"""
return inner @wrapper
def func():
pass func()
游戏模拟继续进行
- 就算开挂,我们也得选完英雄,才能进入游戏,所以我们给基础函数传个参数,但是我们真正执行的是装饰器内部的inner包装函数,所以也要给inner传入参数
def wrapper(fn): # 装饰器雏形
def inner(hero): # 套到装饰器中内层包装函数参数
print("开启外挂!")
fn(hero) # 基础函数参数
print("关闭外挂!")
return inner @wrapper
def play_lol(hero): # 基础函数参数
print("登陆游戏")
print("开始排位...")
print(f"选择英雄:{hero}")
print("游戏中...")
print("胜利!!!")
print("结束游戏") play_lol("盖伦") 运行结果:
开启外挂!
登陆游戏
开始排位...
选择英雄:盖伦 # 传入的参数
游戏中...
胜利!!!
结束游戏
关闭外挂!
- 虽然开挂了,但是还是会遇到巨坑无敌坑的队友,我们得把他记下来举报他,所以要给基础函数填写返回值,同时给真正执行的inner函数填写返回值
def wrapper(fn): # 装饰器雏形
def inner(hero):
print("开启外挂!")
ret = fn(hero) # 接收基础函数的返回值
print("关闭外挂!")
return ret # 返回包装后的函数的返回值
return inner @wrapper
def play_lol(hero): # 基础函数参数
print("登陆游戏")
print("开始排位...")
print(f"选择英雄:{hero}")
print("游戏中...")
print("胜利!!!")
print("结束游戏")
return "坑比队友:xxx" # 基础函数返回值 print(play_lol("盖伦")) 运行结果:
开启外挂!
登陆游戏
开始排位...
选择英雄:盖伦
游戏中...
胜利!!!
结束游戏
关闭外挂!
坑比队友:xxx # 返回值
- 到此,我们装饰器标准模式也就出来了
装饰器标准模式(非常重要)
def wrapper(fn):
def inner(*args, **kwargs):
"""扩展功能"""
ret = fn(*args, **kwargs)
"""扩展功能"""
return ret
return inner @wrapper
def func():
pass func()
Python基础(十三)的更多相关文章
- Python基础(十三) 为什么说python多线程没有真正实现多现程
Python中的多线程没有真正实现多现程! 为什么这么说,我们了解一个概念,全局解释器锁(GIL). Python代码的执行由Python虚拟机(解释器)来控制. Python在设计之初就考虑要在主循 ...
- python基础(十三) cmd命令调用
python cmd命令调用 关于python调用cmd命令: 主要介绍两种方式: 1.python的OS模块. OS模块调用CMD命令有两种方式:os.popen(),os.system(). 都是 ...
- python 基础(十三) time模块
日期和时间 一.time模块 import time 时间戳: 时间戳是指格林威治时间1970年1月1日0时0分0秒至现在的秒数 s(秒).ms(毫秒).μs(微秒).ns(纳秒), 其中:1 ...
- python基础十三之内置函数
内置函数 操作字符串代码 eval和exec print(eval('1+2')) # 简单的计算 有返回值 exec('for i in range(10):print(i)') # 简单的流程控制 ...
- 二十三. Python基础(23)--经典类和新式类
二十三. Python基础(23)--经典类和新式类 ●知识框架 ●接口类&抽象类的实现 # 接口类&抽象类的实现 #①抛出异常法 class Parent(object): ...
- 十三. Python基础(13)--生成器进阶
十三. Python基础(13)--生成器进阶 1 ● send()方法 generator.send(value) Resumes the execution, and "sends&qu ...
- Python基础学习笔记(十三)异常
参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-exceptions.html Python用异常对象(excep ...
- (Python基础教程之十三)Python中使用httplib2 – HTTP GET和POST示例
Python基础教程 在SublimeEditor中配置Python环境 Python代码中添加注释 Python中的变量的使用 Python中的数据类型 Python中的关键字 Python字符串操 ...
- 第十三章 Python基础篇结束章
从2019年3月底开始学习Python,4月份开始在CSDN发博客,至今不到半年,老猿认为博客内容中关于Python基础知识的内容已经基本告一段落,本章进入Python基础知识结束章节,对Python ...
- Day1 - Python基础1 介绍、基本语法、流程控制
Python之路,Day1 - Python基础1 本节内容 Python介绍 发展史 Python 2 or 3? 安装 Hello World程序 变量 用户输入 模块初识 .pyc是个什么鬼 ...
随机推荐
- 2013-2014 ACM-ICPC Pacific Northwest Regional Contest D.Delta Quadrant
题意略. 思路: 由于这是一颗无根树,我们可以贪心地来删去边. 详见代码: #include<bits/stdc++.h> using namespace std; typedef lon ...
- Java集合框架之LinkedList浅析
Java集合框架之LinkedList浅析 一.LinkedList综述: 1.1LinkedList简介 同ArrayList一样,位于java.util包下的LinkedList是Java集合框架 ...
- jdk13快来了,jdk8的这几点应该看看!
说明 jdk8虽然出现很久了,但是可能我们还是有很多人并不太熟悉,本文主要就是介绍说明一些jdk8相关的内容. 主要会讲解: lambda表达式 方法引用 默认方法 Stream 用Optional取 ...
- Codeforces Round #465 &935C. Fifa and Fafa计算几何
传送门 题意:在平面中,有一个圆,有一个点,问能在这个圆中围出最大的圆的圆心坐标和半径.要求这个最大圆不包含这个点. 思路:比较基础的计算几何,要分三种情况,第一种就是这个点在圆外的情况.第二种是点在 ...
- UVA - 10462-Is There A Second Way Left? Kruskal求次小生成树
UVA - 10462 题意: 求次小生成树的模板题,这道题因为有重边的存在,所以用kruskal求比较好. #include <iostream> #include <cstdio ...
- Codeforces Round #383 (Div. 2)D. Arpa's weak amphitheater and Mehrdad's valuable Hoses(dp背包+并查集)
题目链接 :http://codeforces.com/contest/742/problem/D 题意:给你n个女人的信息重量w和美丽度b,再给你m个关系,要求邀请的女人总重量不超过w 而且如果邀请 ...
- codeforces 459 E. Pashmak and Graph(dp)
题目链接:http://codeforces.com/contest/459/problem/E 题意:给出m条边n个点每条边都有权值问如果两边能够相连的条件是边权值是严格递增的话,最长能接几条边. ...
- poj 2912 Rochambeau(枚举+带权并查集)
题目链接:http://poj.org/problem?id=2912 题意:多个人玩石头剪刀布分成3组和一个裁判,每一组提前选定了自己出哪个手势,裁判可以随意出什么手势,问是否能够从给出的一系列石头 ...
- Kafka入门宝典(详细截图版)
1.了解 Apache Kafka 1.1.简介 官网:http://kafka.apache.org/ Apache Kafka 是一个开源消息系统,由Scala 写成.是由Apache 软件基金会 ...
- 良许 | 听说,有个同事因为关闭服务器被打进 ICU ……
提问:你是如何关闭电脑的? 普通青年 文艺青年 二逼青年 你是属于哪一种呢? 实话说, 这三种良许都干过~ 还好我没有对服务器这么做, 否则-- 分分钟被打进 ICU -- 1. 关机命令知多少 对于 ...