闭包_装饰器_深浅拷贝
Created|Updated
|Post Views:
闭包背景介绍
1 | """ |
闭包入门



1 | # 定义一个用于求和的闭包。 |
nonlocal关键字介绍
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19"""
nonlocal:
他是一个python内置的关键字,可以实现在内部函数中,修改外部函数的变量值
编写一个闭包,让内部函数去访问外部函数内的参数a = 100,观察效果。
"""
def fn_outer():
a = 100 # 有嵌套
def fn_inner():
# a = a + 1 # 有引用 #生命周期不同
nonlocal a
a=a+1
print(f"a:{a}")
return fn_inner
if __name__ == '__main__':
a = fn_outer()
a() # 101
a() #102
a() #103
装饰器入门
图解
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44# 例如,发表评论前,都是需要先登录的。
# 先定义有发表评论的功能函数,然后在不改变原有函数的基础上,
# 需要提示用户要先登录。
"""
装饰器介绍:
概述/作用:
他的本质是一个闭包函数,目的:就是在不改变原有函数的基础上,对其功能做了增强
前提条件:
1:有嵌套
2:有引用
3:有返回
4:有额外功能
装饰器的用法:
格式1:传统写法
装饰后的函数名=装饰器名(被装饰的原函数名)
装饰后的函数名() !!!一般修饰前和修饰后的函数名保持一致
格式2:语法糖 (推荐)
在要被装饰的原函数头上,直接@装饰器名,直接调用原函数即可
"""
# 编写装饰器
def check_login(fn_name):
# 1.1定义内部函数
def fn_inner(): # 有嵌套
# 1.2增加额外功能
print("校验登录.....登录成功") # 有引用
# 1.3访问原函数 , 即外部函数的引用
fn_name()
# 1.4返回内部函数对象(名)
return fn_inner
@check_login
def comment():
print("发表评论")
if __name__ == '__main__':
#1:传统写法
# 装饰后的函数名 = 装饰器名(被装饰的原函数名)
# 装饰后的函数名() !!!一般修饰前和修饰后的函数名保持一致
#!!!一般修饰前和修饰后的函数名保持一致
# # 修饰后 修饰前
# comment = check_login(comment)
# comment()
#2:语法糖
comment()
装饰器案例
场景1: ==无参无返回值的原函数==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68"""
在无参无返回值的原有函数求和
计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...。
细节:
1:装饰的内部函数格式要和被装饰器的原函数保持一致
解释:
1-1:原函数是无参无返回值的。则装饰器的内部函数也必须是无参无返回值。
1-2:原函数是有参有返回值的,则装饰器的内部函数必须也是有参有返回值。
"""
# 添加一个友好提示(注意:不能改变源码):正在努力计算中...。
# 完成一个可以有提示功能的装饰器
# 【装饰器】
def my_sum(fn_name):
def fn_inner():
print("正在努力计算中.....")
fn_name()
return fn_inner
# 【原函数】在参无返回值的原有函数求和
def get_sum():
a = 10
b = 20
sum = a + b
print(f"sum求和结果:{sum}")
"""
在无参无返回值的原有函数求和
计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...。
细节:
1:装饰的内部函数格式要和被装饰器的原函数保持一致
解释:
1-1:原函数是无参无返回值的。则装饰器的内部函数也必须是无参无返回值。
1-2:原函数是有参有返回值的,则装饰器的内部函数必须也是有参有返回值。
"""
# 添加一个友好提示(注意:不能改变源码):正在努力计算中...。
# 完成一个可以有提示功能的装饰器
# 【装饰器】
def my_sum(fn_name):
def fn_inner(): # 有嵌套
print("正在努力计算中.....") # 有额外功能
fn_name() # 有引用
return fn_inner # 有返回值
# 【原函数】在参无返回值的原有函数求和
@my_sum
def get_sum():
a = 10
b = 20
sum = a + b
print(f"sum求和结果:{sum}")
if __name__ == '__main__':
# get_sum = my_sum(get_sum)
# get_sum()
get_sum()场景2: ==有参无返回值的原函数==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 在有参无返回值的原有函数求和计算结果之前,
# 添加一个友好提示(注意:不能改变源码):正在努力计算中...。
# 【装饰器】
def my_sum(fn_name):
def fn_inner(x, y): # 有嵌套
print("正在努力计算中....") # 有增强
fn_name(x,y) # 有引用
return fn_inner # 有返回
# 【原函数】
# @my_sum
def get_sum(a, b):
sum = a + b
print(f"sum求和结果:{sum}")
if __name__ == '__main__':
# get_sum(10,20)
get_sum=my_sum(get_sum)
get_sum(10,20)场景3:==无参有返回值装饰器==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32"""
在无参有返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...。
在有参有返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...。
"""
# 在无参有返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...。
# 【装饰器】 有嵌套、 有引用 、有返回值、有增加功能
def my_sum(fn_name):
def fn_inner():
print("正在努力计算中...。")
return fn_name() # 有引用
return fn_inner
# 【原函数】
# @my_sum
def get_sum(): # 无参有返回值
a = 10
b = 20
return a + b
# 两种写法
# 传统写法
get_sum=my_sum(get_sum)
print(get_sum())
# 语法糖
# print(get_sum())
# gs = get_sum()
# print(gs)场景4:==有参有返回值装饰器==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 在有参有返回值的原有函数求和计算结果之前,
# 添加一个友好提示(注意:不能改变源码):正在努力计算中...。
# 【装饰器】
def my_sum(fn_name):
def fn_inner(x,y):
print("正在努力计算中...。")
return fn_name(x,y) #有返回值,有引用
return fn_inner
@my_sum
# 【原函数:有参、有返回值】
def get_sum(a, b):
return a + b
print(get_sum(20, 20))场景5:==不定长参数装饰器==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33# 定义一个可以计算多个数据和多个字典value值之和的函数,并调用。
# 在原有函数的计算结果之前,加一个友好提示(不能改变源码):正在努力计算中...。
# 【2.装饰器】
def my_sum(fn_name):
def fn_inner(*args,**kwargs):
print("正在努力计算中...。")
return fn_name(*args,**kwargs)
return fn_inner
# 【1.原函数】
#取结果之和
# @my_sum
def get_sum(*args,**kwargs):
# args 数据元组
# **kwargs 字典
# 定义求和计数器
sum= 0
#遍历获取元组中的值
for i in args:
sum+=i
for v in kwargs.values():
sum+=v
return sum
# return sum(args)+sum(kwargs.values())
if __name__ == '__main__':
get_sum=my_sum(get_sum)
# get_sum()
print(get_sum(1, 2, 3, a=4, b=5, c=6)场景6:==多个装饰器装饰一个函数==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45"""
例如,发表评论前,都是需要 先登录用户 再进行 验证码验证。
先定义有发表评论的功能函数,然后在不改变原有函数的基础上,需要先检查用户登录和输入验证码。
记忆: 多个装饰器装饰一个原函数,就按照 由内向外的顺序来装饰
但是你要用装饰器写法来做的话,看到的效果是从上往下执行
"""
# 【装饰器check_login】
def check_login(fn_name):
def fn_inner():
print("校验登录")
fn_name()
return fn_inner
# 【装饰器check_code】
def check_code(fn_name):
def fn_inner():
print("校验验证码")
fn_name()
return fn_inner
# 【原函数:comment】
def comment():
print("发表评论")
# 【测试】
# 传统方法
# A
comment = check_code(comment)
comment=check_login(comment)
comment()
# B
# comment=check_login(comment)
# comment = check_code(comment)
# comment()
# comment()场景7:==一个装饰器装饰多个函数==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25"""
定义一个既能装饰减法运算,又能装饰加法运算的装饰器,即带有参数的装饰器。
记忆:
1:1个装饰器的参数有且只能有1个
2:如果装饰器有多个参数,可以在该装饰器外面在包裹一层装饰器,把该装饰器当做内部函数即可
"""
# 【装饰器】
def my_sum_sub(fn_name):
def fn_inner(a,b):
print("正在努力计算中.....")
return fn_name(a,b)
return fn_inner
# 【原函数】
@my_sum_sub
def get_sum(a, b):
return a + b
@my_sum_sub
def get_sub(a, b):
return a - b
if __name__ == '__main__':
print(get_sum(10,20))
print(get_sub(10, 20))场景8:==一个装饰器装饰多个函数嵌套版==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32# 【装饰器】
def logging(flag):
# 定义内部函数 ,格式要和原函数保持一致 参数和返回值
def my_sum_sub(fn_name):
def fn_inner(a, b):
if flag == "+":
print("正在努力计算【加法】中")
elif flag == "-":
print("正在努力计算【减法】中")
return fn_name(a, b)
return fn_inner
return my_sum_sub
# 【原函数】
# @logging('+')
def get_sum(a, b):
return a + b
# @logging('-')
def get_sub(a, b):
return a - b
#
#
# # __name___
# sub_str = logging("+")
# get_sum_sub = sub_str(get_sub)
# print(get_sum_sub(10, 5))场景9:==一个装饰器装饰多个函数魔法方法==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 【装饰器】
def my_sum_sub(fn_name):
def fn_inner(a, b):
if fn_name.__name__ == "get_sum":
print("正在努力计算【加法】中")
elif fn_name.__name__ == "get_sub":
print("正在努力计算【减法】中")
return fn_name(a, b)
return fn_inner
# 【原始】
@my_sum_sub
def get_sum(a, b):
return a + b
@my_sum_sub
def get_sub(a, b):
return a - b
if __name__ == '__main__':
print(get_sum(10, 20))
print(get_sub(30,60))
Author: 甘虎文
Copyright Notice: All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Related Articles
2023-09-12
数据结构与算法
冒泡排序_思路及代码 123456789101112131415161718192021222324252627282930313233343536373839404142"""排序算法稳定性介绍: 概述:排序算法= 把一串数据按照升序或者降序的方式进行排列 方法、思维、方式 分类: 稳定排序算法 排序后,想同元素的相对位置不发生改变 不稳定排序算法 排序后,想同元素的相对位置发生改变 冒泡排序: 原理:相邻元素两两比较,大的往后走,这样第一轮比较厚,最大值在最大索引处 重复该动作,只要排序完毕。 核心: 1:比较的总轮数 列表的长度-1 2:每轮比较的总次数 列表的长度-1-当前轮数的索引 3:谁和谁比较 ? 列表[j]和列表[j+1] 分析流程: 假设元素个数5个 ,具体如下: [5,3,4,7,2] 长度为5 比较的轮数,i...
2023-09-29
数据结构与算法1
数据结构和算法简介 数据结构 就是存储和组织数据的方式, 分为: 线性结构 和 非线性结构 算法 就是解决问题的思路和发放, 它具有独立性, 即: 它不依赖语言, 而是解决问题的思路. Java能做, Python也能做. 特性 有输入, 有输出, 有穷性, 确定性, 可行性. 如何衡量算法的优劣 ==大O标记法,== 即: 将次要条件都省略掉, 最终形成1个表达式. **主要条件:**随着问题规模变化而==变化==的. **次要条件:**随则问题规模变化而==不变==的. 最优和最坏时间复杂度 如非特殊说明, 我们考虑的都是 最坏时间复杂度, 因为它是算法的一种保证. 而最优时间复杂度是算法的 最理想, 最乐观的状况, 没有太大的参考价值. 常见的时间复杂度如下 从最优到最坏分别是: O(1) -> O(logn) -> O(n) -> O(n logn) -> O(n²) -> O(n³) ...
2023-08-09
生成器_正则
多线程特点_随机性12345678910111213141516171819202122import threading# 使用多线程来模拟小明一边编写num行代码,一边听count首音乐功能实现。def coding(name, num): for i in range(num): print(f"{name}正在敲{i}行代码")def music(name, count): for i in range(count): print(f"{name}正在听{i}遍音乐")# args 元组# kwargs 字典if __name__ == '__main__': t1 = threading.Thread(target=coding, args=("小明", 100)) t2 = threading.Thread(target=music, args=("小...
2023-10-31
网络编程_进程_线程
网络编程介绍 概述 就是用来实现网络互联的 不同计算机上 运行的程序间 可以进行数据交互. 三要素 IP地址: 设备(电脑, 手机, Ipad…)在网络中的唯一标识 分类: IPV4, 4字节, 十进制. 例如: 192.168.88.100 IPV6, 8字节, 十六进制, 宣传语: 可以让地球上的每一粒沙子都有自己的IP 两个DOS命令: 查看IP: windows: ipconfig Linux, Mac: ifconfig 测试网络连接: ping ip地址 或者 域名 端口号: 程序在设备(电脑, 手机, Ipad…)上的唯一标识. 范围: 0 ~ 65535, 其中0 ~ 1023已经被系统占用或者用作保留端口, 自定义端口时尽量规避这个范围. 协议: 传输规则, 规范. 常用的协议: TCP(这个用的最多) 和 UDP TCP特点: 1.面向有连接 2.采用字节流传输数据, 理论无大小限制. 3.安全(可靠)...
2023-06-03
面向对象基础
面向对象和面向过程 编程思想 就是人们利用计算机来解决问题的思维. 分类 面向过程 它是一种编程思想, 强调的是以 步骤(过程) 为基础完成各种操作. 面向对象 参考思路: 概述, 思想特点, 举例, 总结 它是一种编程思想, 强调的是以 对象 为基础完成各种操作, 它是基于 面向过程的.说到面向对象, 不得不提的就是它的三大思想特点: 1. 更符合人们的思考习惯. 2. 把复杂的事情简单化. 3. 把人们(程序员)从执行者变成指挥者. 举例: 越符合当时的场景越好, 例如: 买电脑, 洗衣服… 总结: 万物接对象. 面向对象特征介绍 三大特征 封装 继承 多态 封装简介 概述 就是隐藏对象的属性和实现细节, 仅对外提供公共的访问方式. 举例 电脑, 手机, 函数, 类 = 属性 + 行为 好处 提高代码的安全性. (私有化) 提高代码的复用性. (函数) 继承 概述 子类继承父类的成员, 例如: 属性, 行为等.大白话: 子承父业. 好处 提高代码的复用性. 多态...
2023-07-07
面向对象进阶
子类重写父类的功能12345678910111213141516171819202122232425262728293031323334353637# 小明掌握了老师傅和黑马的技术后,# 自己潜心钻研出一套自己的独门配方的全新摊煎饼果子技术。"""重写解释: 概念:重写也叫覆盖,即子类出现和父类【重名】的属性或者方法 。称之为重写 调用层次:就近原则 子类用子类的,没有就去找父类的,依次按照继承顺序去找其他父类"""class Master: def __init__(self): self.kongfu = "[古法煎饼果子配方]" def make_cake(self): print(f"运用{self.kongfu}制作煎饼果子")class School: def __init__(self): self.kongfu = "[黑马AI煎饼果子配方]" de...