1 函数概述
- 函数也是对象。
- 函数可以动态添加属性。
- 函数可以赋值给变量。
- 函数也可以作为参数。
- 函数也可以作为返回值。
- 代码示例
-
# 1 函数也是对象 print('-----------1----------') a1 = 100 # int类的实例对象 a2 = 'hello' # str类的实例对象 a3 = [10, 20, 30] # list类的实例对象 def welcome(): # welcome函数是function类的实例对象 print('hello world') print(type(a1)) print(type(a2)) print(type(a3)) print(type(welcome)) # 2 函数可以动态添加属性 print('-----------2----------') welcome.desc = 'python' # 添加属性 welcome.version = 1.0 # 添加属性 print(welcome.desc) # 调用 print(welcome.version) # 调用 # 3 函数可以赋值给变量 print('-----------3----------') say_hello = welcome # 赋值 say_hello() # 调用 # 4 可变参数与不可变参数 print('-----------4----------') ## 4.1 不可变参数 print('----------4.1----------') b1 = 10088 def welcome_f1(data): print('welcome_f1 data: ', data) data = 10011 # 此处data是新实例对象 print('data: ', data) welcome_f1(b1) print('b1: ', b1) ## 4.2 可变参数 print('----------4.2----------') b2 = [10, 20, 30] def welcome_f2(data): print('welcome_f2 data: ', data) data[2] = 99 print('data: ', data) welcome_f2(b2) print('b2: ', b2) # 5 函数也可以作为参数 print('-----------5----------') def welcome_param(): print('welcome_param 调用') def caller(f): print('caller 调用') f() caller(welcome_param) # 6 函数也可以作为返回值 print('-----------6----------') def welcome_return(): print('welcome_return') def show_msg(msg): print('msg: ', msg) return show_msg res1 = welcome_return() res1('Python or C++') 
-
2 函数的多返回值
- 在
return关键字后面写多个值,并且多个值之间用逗号隔开,Python 会自动把多个值打包成元组。 - 示例代码
-
def calculate(x, y): res1 = x + y res2 = x - y return res1, res2 result = calculate(10, 28) print('result: ', type(result), result) r1, r2 = calculate(10, 28) print('r1: ', type(r1), r1) print('r2: ', type(r2), r2) 
-
3 参数的打包与解包
- 定义函数时,打包接收参数:
*形参名:打包所有的位置参数(会形成一个元组)。**形参名:打包所有的关键字参数(会形成一个字典)。
- 调用函数时,解包传递参数:
*变量名:将元组拆解成一个一个独立的位置参数。**变量名:将字典拆解一个一个key=value形式的关键字参数。
- 代码示例
-
# 打包接收参数 # *args: 打包所有位置参数(会形成一个元组) # **kwargs: 打包所有关键字参数(会形成一个字典) print('------------打包接收参数-----------') def show_info1(*args, **kwargs): print('args: ', args) print('kwargs: ', kwargs) show_info1(10, 20, 30, name='张三', age=20) # 解包传递参数 # *变量名: 将元组拆解成一个一个独立的位置参数 # **变量名: 将字典拆解成一个一个 key=value 形式的关键字参数 print('------------解包传递参数-----------') def show_info2(num1, num2, num3, name, age): print('num1: ', num1) print('num2: ', num2) print('num3: ', num3) print('name: ', name) print('age: ', age) nums = (1, 2, 3) person = {'name': '小明', 'age': 18} show_info2(*nums, **person) print('------------二者结合使用-----------') def show_info3(*args, **kwargs): print('args: ', args) print('kwargs: ', kwargs) nums = (1, 2, 3) person = {'name': '小明', 'age': 18} show_info2(*nums, **person) 
-
4 高阶函数
- 当一个函数的『参数是函数』或者『返回值是函数』那该函数就是『高阶函数』。
- 示例代码
-
def info(msg): return '[提示]: ' + msg def warn(msg): return '[警告]: ' + msg def error(msg): return '[错误]: ' + msg def log(func, text): print(func(text)) log(info, '文件保存成功') log(warn, '磁盘空间不足') log(error, '文件保存失败') 
-
5 条件表达式
- 表达式:执行后最终能得到一个值的代码,就是表达式。表达式最终会形成一个值,可以写在任何一个需要值的地方。
- 条件表达式: 根据不同的条件,得到不同的值,又称:三元表达式,也叫:三目运算符。
- 语法格式:
结果1 if 条件 else 结果2 - 具体规则: 如果条件为真,整个表达式的结果就是“结果1”,否则就是“结果2”。
- 语法格式:
- 示例代码
-
# 表达式 a1 = 3 + 5 a2 = 'abc' * 2 a3 = 5 > 3 a4 = 'y' in 'python' a5 = len('hello world') # 条件表达式 age = 18 text='成年' if age >= 19 else '未成年' print('text:', text) 
-
6 匿名函数
- 所谓『匿名函数』,就是没有名字的函数,它无需使用def关键字去定义。
- Python 中使用lambda关键字来定义『匿名函数』,格式为:
lambda 参数: 表达式。 - 使用场景: 当一个函数只用一次、只做一点点小事,使用匿名函数会更简洁。
- 匿名函数特点
- 只能写一行,不能写多行代码。
- 不能写代码块(if、for、while)
- 冒号右边必须是表达式,且只能写一个表达式。
- 执行结果自动作为返回值。
- 示例代码
-
# 使用普通函数实现计算效果 def add(a, b): return a + b def sub(a, b): return a - b def calculate(func, x, y): print(f'计算结果为: {func(x, y)}') calculate(add, 2, 3) calculate(sub, 4, 5) # 匿名函数 add2 = lambda a, b: a + b add3 = lambda: 'this is add3' res2 = add2(20, 48) print('res2:', res2) res3 = add3() print('res3:', res3) calculate(lambda a, b: a + b, 8, 9) # 应用 is_adult = lambda age: '成年' if age >= 18 else '未成年' print(is_adult(23)) 
-
7 几个数据处理函数
7.1 map函数
- 对一组数据中的每一个元素,统一执行某种操作(加工),并生成一组新数据。
- 示例代码
-
print('------------示例1---------------') nums = [10, 20, 30, 40] def double_func(x): return x * 2 # map函数返回值是一个迭代器对象,需要我们自己手动去遍历,或者手动类型转换 res1 = map(double_func, nums) print('res1:', list(res1)) # for item in res1: # print(item) print('------------示例2---------------') names=('python', 'java', 'js') res2 = map(lambda x: x.upper(), names) print('res2:', tuple(res2)) print('------------示例3---------------') str_number = {'12', '22', '32'} res3 = map(int, str_number) print('res3:', set(res3)) # 注意点: # 1.延迟执行:map 不会立刻计算,只有在“需要结果”时才执行计算。 # 2.返回的是迭代器对象,且一旦遍历完成,就会被“耗尽”。 # 3.map不会影响元素数量。 print('------------示例4---------------') nums2 = [10, 20, 30, 40] res4 = map(lambda x: x * 2, nums2) print('res4:', list(res4)) print('res4:', list(res4)) 
-
7.2 filter 函数
- 从一组数据中,筛选出符合条件的元素(过滤),并组成一组新数据。
- 示例代码
-
print('------------示例1---------------') nums1 = [10, 20, 30, 40, 50] res1 = filter(lambda n: n > 30, nums1) print('res1:', list(res1)) print('------------示例2---------------') person1 = [ {'name': '张三', 'age': 16}, {'name': '李四', 'age': 14}, {'name': '王五', 'age': 20}, {'name': '赵六', 'age': 25}, ] res2 = filter(lambda p: p['age'] > 18, person1) print('res2:', list(res2)) print('------------示例3---------------') names = ['张三', '', '李四', None, '王五'] res3 = filter(lambda n: n, names) print('res3:', list(res3)) # 注意点 # 1.延迟执行:filter不会立刻筛选,只有在“需要结果”时才执行。 # 2.返回的是迭代器对象,且一旦遍历完成就会被“耗尽”。 # 3.filter可能会影响元素数量。 # filter函数的特殊用法:如果不传递过滤函数,那么自动会过滤掉“假值” print('------------示例4---------------') data1 = [0, 1, '', 'hello', [], (1, 2), 5] res4 = filter(None, data1) print('res4:', list(res4)) 
-
7.3 sorted 函数
- 对一组数据进行排序,返回一组新数据。
- 示例代码
-
print('------------示例1---------------') # 默认从小到大排序, reverse=True设置从大到小排序 nums1 = [100, 20, 3, 521, 222] res1 = sorted(nums1, reverse=True) print('res1:', res1) print('------------示例2---------------') # 按照字符串长度排序 name1 = ['python', 'js', 'c++', 'java'] res2 = sorted(name1, key=lambda n: len(n)) # res2 = sorted(name1, key=len) print('res2:', res2) print('------------示例3---------------') # 根据字典中的某个字段进行排序 person1 = [ {'name': '张三', 'age': 16}, {'name': '李四', 'age': 14}, {'name': '王五', 'age': 20} ] res3 = sorted(person1, key=lambda p: p['age']) print('res3:', res3) print('------------示例4---------------') res4 = max(person1, key=lambda p: p['age']) print('res4:', res4) 
-
7.4 reduce 函数
- 将一组数据不断“合并”,最终归并成一个结果。
- 示例代码
-
from functools import reduce print('------------示例1---------------') # 数值统计-求和 nums1 = [1, 2, 3, 4, 5] def count_func(a, b): return a + b res1 = reduce(count_func, nums1) # 可以传初始值 res2 = reduce(count_func, nums1, 100) print('res1:', res1) print('res2:', res2) print('------------示例2---------------') res3 = reduce(lambda a, b: a * b, nums1, 1) print('res3:', res3) print('------------示例3---------------') # 字符串拼接 str_list1 = ['ab', 'cd', 'ef'] res4 = reduce(lambda a, b: a + b, str_list1) print('res4:', res4) 
-
8 列表推导式
- 用一条简洁语句,从可迭代对象中,生成新列表的语法结构。
- 语法格式:
[ 表达式 for 变量 in 可迭代对象 ] - 示例
-
# 需求:让列表中每个元素,都变为原来的2倍,得到的是一个新的列表 # 方式1:用map函数 mums1 = [10, 20, 30, 40] res1 = list(map(lambda x: x * 2, mums1)) print('res1:', res1) # 方式2:用 for循环 + append() nums2 = [10, 20, 30, 40] res2 = [] for n in nums2: res2.append(n * 2) print('res2:', res2) # 方式3:用列表推导式 nums3 = [10, 20, 30, 40] res3 = [n * 2 for n in nums3] print('res3:', res3) # 带条件的列表推导式-取出大于20的元素再乘以2形成一个新列表 nums4 = [10, 20, 30, 40] res4 = [n * 2 for n in nums4 if n > 20] print('res4:', res4) # 字典推导式 names = ['张三', '李四', '王五'] scores = [85, 90, 82] res5 = {names[i]: scores[i] for i in range(len(names))} print('res5:', res5) # 集合推导式 names2 = ['张三', '李四', '王五'] res6={n + '!' for n in names2} print('res6:', res6) # Python中没有元组推导式 
-
9 常用内置函数
9.1 输入与输出
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False):输出指定内容。参数含义如下objects:要输出的内容sep:输出多个内容时的分隔符end:结尾追加的内容file:输出位置(默认控制台)flush:是否立即刷新输出
input():获取用户输入- 示例代码
-
import time # 默认打印 print(10, 20, 30, 40) # 指定分隔符 print(10, 20, 30, 40, sep='-') # 指定结尾字符 print(10, 20, 30, 40, sep='-', end='!') # 指定打印位置-打印到文件中 f1 = open('a.txt', 'w', encoding='utf-8') print(10, 20, 30, 40, file=f1) # 进度条效果 print('\n加载中', end='') for index in range(5): print('.', end='', flush=True) time.sleep(0.5) print('完成!', end='', flush=False) for index in range(1, 101): print(f'\r已加载{index}%', end='', flush=True) time.sleep(0.1) 
-
9.2 类型转换
int():转为整数float():转为浮点数str():转为字符串bool():转为布尔值list():转为列表tuple():转为元组set():转为集合dict():转为字典
9.3 数学相关
abs():取绝对值round():银行家舍入法:小于5舍,大于5入,等于5看奇偶(奇入偶舍)pow(a, b):计算 a 的 b 次方pow(a, b, c):计算 a 的 b 次方后,再对 c 取模divmod(a, b):返回 (商, 余数)max():最大值(支持 key)min():最小值(支持 key)sum():求和map():加工一组数据filter():按条件过滤数据reduce():合并计算(需 functools.reduce)sorted():排序(支持 key)- 示例代码
-
# 取绝对值 print('abs(-12):', abs(-12)) # 四舍五入 - 大于5舍、小于5入、等于5看奇偶(奇入偶舍) print('round(3.14):', round(3.14)) print('round(12.88):', round(12.88)) print('round(4.5):', round(4.5)) print('round(5.5):', round(5.5)) print('round(3.1415926, 4):', round(3.1415926, 4)) # 保留四位小数 # 幂运算 print('pow(2, 3): ', pow(2, 3)) # 2的3次方 print('pow(2, -1): ', pow(2, -1)) # 2的-1次方 print('pow(2, 0.5): ', pow(2, 0.5)) # 2的开平方 print('pow(2, 4, 5): ', pow(2, 4, 5)) # 2的4次方对5取模 # 商和余数 print('divmod(10, 3): ', divmod(10, 3)) # 10除以3的商和余数 
-
9.4 数据容器相关
len():获取容器中元素个数range():生成数字序列(常用于循环)enumerate():为序列添加索引zip():将多个序列一一配对- 示例代码
-
# range() for index in range(1, 10): print(index, end=' ') print('\n') # 设置步长 for index in range(1, 10, 2): print(index, end=' ') print('\n') # zip() names = ('张三', '李四', '王五') scores = [80, 90, 88] res1 = zip(names, scores) print('res: ', list(res1)) 
-
9.5 类型判断与对象相关
type():查看对象类型isinstance(obj, type):判断对象是否属于某个类型issubclass(A, B):判断类 A 是否为类 B 的子类id():查看对象的内存地址
9.6 逻辑判断相关
all():所有元素为真时返回 Trueany():至少一个为真返回 True- 示例代码
-
l1 = [10, 'Python', {1, 2, 3}, 0] print(all(l1)) print(any(l1)) 
-
9.7 字符串赋值相关
ord():获取字符的 Unicode 编码值chr():将 Unicode 编码值转换为字符
10 浅拷贝与深拷贝
10.1 直接赋值
-
代码示例
-
# 直接赋值 # 两个变量会指向同一个对象,修改其中一个,会互相影响 nums1 = [10, 20, 30, 40] nums2 = nums1 print('nums1内存地址: ', hex(id(nums1))) print('nums2内存地址: ', hex(id(nums2))) print('修改前 nums1:', nums1) print('修改前 nums2:', nums2) nums2[3] = 99 print('修改后 nums1:', nums1) print('修改后 nums2:', nums2) 
-
-
图片示例
10. 2 浅拷贝
-
浅拷贝会创建一个新的外层容器,但内部的元素仍然引用原来的对象。
-
示例代码
-
import copy nums1 = [10, 20, 30, 40] # 对nums1进行浅拷贝 nums2 = copy.copy(nums1) print('nums1内存地址: ', hex(id(nums1))) print('nums2内存地址: ', hex(id(nums2))) print('修改前 nums1:', nums1) print('修改前 nums2:', nums2) nums2[3] = 99 print('修改后 nums1:', nums1) print('修改后 nums2:', nums2) 
-
-
图片示例
-
浅拷贝存在的问题:嵌套数据仍然是共享的,修改嵌套数据会互相影响。
-
代码示例
-
import copy nums1 = [10, 20, 30, [40, 50]] # 对nums1进行浅拷贝 nums2 = copy.copy(nums1) print('nums1内存地址: ', hex(id(nums1))) print('nums2内存地址: ', hex(id(nums2))) print('修改前 nums1:', nums1) print('修改前 nums2:', nums2) nums2[3][0] = 99 print('修改后 nums1:', nums1) print('修改后 nums2:', nums2) 
-
-
图片示例
10.3 深拷贝
-
创建一个新的外层容器,同时对内部所有【可变对象】进行递归复制(不可变对象不复制,继续引用)。
-
代码示例
-
import copy nums1 = [10, 20, 30, [40, 50]] # 对nums1进行深拷贝 nums2 = copy.deepcopy(nums1) print('nums1内存地址: ', hex(id(nums1))) print('nums2内存地址: ', hex(id(nums2))) print('修改前 nums1:', nums1) print('修改前 nums2:', nums2) nums2[3][0] = 99 print('修改后 nums1:', nums1) print('修改后 nums2:', nums2) 
-
-
图片示例
-
深拷贝特点
- 深拷贝可以彻底消除数据之间的相互影响
- 深拷贝遇到不可变对象不会复制,会直接引用。
-
注意点
-
示例代码
-
import copy # 深拷贝只复制可变对象,不可变对象会直接引用 a = 666 b = copy.deepcopy(a) print('hex(id(a)): ', hex(id(a))) print('hex(id(b)): ', hex(id(b))) # 元组中如果只包含不可变对象,则深拷贝没有效果 nums1 = (10, 20, 30, (40, 50)) nums2 = copy.deepcopy(nums1) print('hex(id(nums1)): ', hex(id(nums1))) print('hex(id(nums2)): ', hex(id(nums2))) 
-
-
深拷贝只复制可变对象,不可变对象会直接引用。
-
元组中如果只包含不可变对象,则深拷贝没有效果。
-
11 四种作用域
-
Python 中有四种作用域,分别是:
-
- Local(局部作用域)
-
- Enclosing(外层作用域)
-
- Global(全局作用域)
-
- Built-in(内建作用域)
-
-
当访问一个变量时,Python 会按以下顺序查找: Local => Enclosing => Global => Built-in
-
代码示例
-
a = 100 def test(b): print('我是test函数') print('test中打印的a是', a) print('test收到的参数b是', b) c = 200 d = 300 print('test中的c和d是', c, d) def inner(): e = 400 nonlocal c # 这样声明后,操作c作用的是外部作用域 c = 999 print('inner中的e是', e) print('inner中打印的c是', c) inner() print('test中的c和d是', c, d) print('全局打印的a是', a) test(66) 
-
-
图片示例
11.1 局部作用域(Local)
- 函数的内部就是局部作用域,局部作用域中的变量,只在该函数内部可见。
- 特点:
- 每次调用函数都会创建一个新的局部作用域。
- 函数运行结束后,局部作用域会随之销毁。
- 局部作用域优先级最高,即:查找一个变量时,Python 会首先在局部作用域中查找。
- 代码示例
-
def test(): x = 10 # x 在局部作用域 print(x) # 可以访问 test() print(x) # ❌ 报错:x 在局部之外不可见
-
11.2 外层作用域(Enclosing)
- 如果函数中又定义了函数,那么外层函数的作用域,就是内层函数的 Enclosing 作用域。
- 特点:
- 只有当函数“嵌套定义”时才会出现。
- 内层函数可以读取外层函数变量。
- 想修改外层变量必须使用nonlocal。
- 示例代码
-
def outer(): y = 20 def inner(): nonlocal y y = 99 print('inner y: ', y) print('outer y: ', y) inner() print('outer y: ', y) outer() 
-
11.3 全局作用域(Global)
.py文件就是全局作用域,全局作用域中的变量,在当前.py文件的任何位置都可以访问。- 特点:
- 全局变量只在当前.py文件中可见。
- 函数内部可以使用global关键字修改全局变量。
- 示例代码
-
a = 100 def test(): global a a = 200 # 修改全局变量 print(a) test() print(a) 
-
11.4 内建作用域(Built-in)
- Python 预先定义好的东西,会放在内建作用域中,所有
.py文件都可以直接使用。 - 特点:
- 所有.py文件都能直接使用其中的名称。
- 例如:print、len、range、sum、max 等。
- 查找优先级最低,即:查找一个变量时,内建作用域是“最后一道防线”。
12 闭包
12.1 前置知识
- 前置知识 1: 局部作用域的生命周期。
- 每次调用函数时,都会创建一个新的局部作用域。
- 函数执行完毕后,该作用域就会被销毁,其中的局部变量,也会随之释放。
- 前置知识 2:内层函数访问外层变量。
- 【内层函数】可以访问到【外层函数】作用域中的变量。
- 访问外层变量不用nonlocal,修改外层变量时要使用nonlocal。
- 示例代码
-
# 每次调用函数时,都会创建一个新的局部作用域。 # 函数执行完毕后,该作用域就会被销毁,其中的局部变量,也会随之释放。 def outer(): num = 10 num += 1 print('outer() 中的num:', num) # 两次打印结果一样 outer() outer() # 【内层函数】可以访问到【外层函数】作用域中的变量。 # 访问外层变量不用nonlocal,修改外层变量时要使用nonlocal。 def outer_2(): num = 10 def inner(): nonlocal num num = 99 print('inner() 中的num:', num) inner() print('outer_2() 中的num:', num) outer_2() 
-
12.2 闭包概念
- 闭包 = 内层函数 + 被内层函数引用的外层变量。
- 产生闭包的三个条件如下
-
- 必须有函数嵌套
-
- 内层函数使用了外层函数的变量
-
- 外层函数返回内层函数
-
- 示例代码
-
# 闭包 = 内层函数 + 被内层函数所引用的外层变量 # 闭包产生的条件 # 1. 要有函数嵌套 # 2. 内层函数中使用了外层函数的变量 # 3. 外层函数要返回内层函数 - 只有返回内层函数,闭包才能“活下来” def outer(): num = 10 score = 0.5 msg = 'hello world' print('num 的地址:', hex(id(num))) print('score 的地址:', hex(id(score))) def inner(): nonlocal num num += 1 print('inner() 中的num:', num) print('inner() 中的score:', score) return inner f_inner = outer() # outer函数中,被inner所使用到的变量,会被封存到【闭包单元(cell)】中。 # 这些cell会组成一个__closure__元组,最终放在了 inner 函数身上。 print(f_inner.__closure__) f_inner() f_inner() f_inner() print('闭包单元中的num:', f_inner.__closure__[0].cell_contents) print('闭包单元中的score:', f_inner.__closure__[1].cell_contents) 
-
12.3 闭包注意点
- 调用n次外层函数,会得到n个不同的闭包,并且这些闭包之间互不影响。
- 内层函数中用到的外层变量是可变对象,多个闭包之间依然互不影响。
- 示例代码
-
# 调用n次外层函数,会得到n个不同的闭包,并且这些闭包之间互不影响 print('**************示例1******************') def outer(): num = 10 print('num 的地址:', hex(id(num))) def inner(): nonlocal num num += 1 print('inner() 中的num:', num) return inner f1 = outer() print(f1.__closure__) f1() f1() f1() f2=outer() print(f1.__closure__) f2() f2() # 内层函数中用到的外层变量是可变对象,多个闭包之间依然互不影响 print('**************示例2******************') def outer_2(): nums = [] def inner_2(value): nonlocal nums nums.append(value) print('inner_2() 中的nums:', nums) return inner_2 f3 = outer_2() f3(10) f3(20) f3(30) f4 = outer_2() f4(40) f4(50) 
-
12.4 闭包优点
- 可以“记住”状态:不用全局变量,也不用写类,就能在多次调用之间保存数据。
- 可以做“配置过的函数”:先传一部分参数,把环境固定住,得到一个定制版函数。
- 可以实现简单的“数据隐藏”:外层变量对外不可见,只能通过内层函数访问。
- 是装饰器(decorator)等高级用法的基础。
- 示例代码
-
# 可以“记住”状态:不用全局变量,也不用写类,就能在多次调用之间保存数据。 # 可以做“配置过的函数”:先传一部分参数,把环境固定住,得到一个定制版函数。 def beauty(char, n): def show_msg(msg): print(char * n + msg + char * n) return show_msg show_1 = beauty('*', 3) show_1('hello') show_1('world') show_1 = beauty('&', 5) show_1('welcome') show_1('BeiJing') # 可以实现简单的“数据隐藏”:外层变量对外不可见,只能通过内层函数访问。 # 是装饰器(decorator)等高级用法的基础。 
-
12.5 闭包缺点
- 理解成本较高:对初学者不太友好,滥用会让代码难读。
- 如果闭包里引用了很大的对象,又长期不释放,可能会增加内存占用。
- 很多场景下,其实用【类 + 实例属性】会更清晰,闭包不一定是最优解。
- 示例代码
-
# 理解成本较高:对初学者不太友好,滥用会让代码难读。 # 如果闭包里引用了很大的对象,又长期不释放,可能会增加内存占用。 # 很多场景下,其实用【类 + 实例属性】会更清晰,闭包不一定是最优解。 class Beauty: def __init__(self, char, n): self.char = char self.n = n def show_msg(self, msg): print(self.char * self.n + msg + self.char * self.n) b1 = Beauty('*', 4) b1.show_msg('hello') b2 = Beauty('$', 3) b2.show_msg('world')
-
13 装饰器
- 概述:装饰器是一种在【不修改原函数代码】的前提下,对函数进行【增强】的工具。 它是 Python 中非常强大的语法特性,常用于:日志、校验、计时、缓存、权限控制等。
- 核心语法: 装饰器是一种可调用对象(通常是函数),接收一个函数作为参数,并返回一个新函数。
13.1 函数装饰器
-
定义装饰器核心规则:
- 接收被装饰的函数、同时返回新函数(wrapper)。
- 装饰器“吐出来”的是 wrapper 函数,以后别人调用的也是 wrapper 函数。
- 为了保证参数的兼容性,wrapper 函数要通过 *args 和 **kwargs 接收参数。
- wrapper 函数中主要做的是:调用原函数(被装饰的函数)、执行其它逻辑,但要记得将原函数的返回值 return 出去。
-
应用:在不改变原函数的前提下,给函数统一加上:日志、计时、校验、缓存等功能。
-
示例代码1:手动装饰
-
def say_hello(func): def wrapper(*args, **kwargs): print('hello world') return func(*args, **kwargs) return wrapper def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res # 正常调用add函数 print('***********正常调用************') res1 = add(1, 2) print('res1:', res1) # 需求: 在不修改add函数的情况下,给add函数增加一些额外的功能 # 实现方案: 使用装饰器 print('***********使用装饰器(手动装饰)************') add = say_hello(add) # 手动装饰(不推荐) res2 = add(1, 2) print('res2:', res2) 
-
-
示例代码2:语法糖@
-
def say_hello(func): def wrapper(*args, **kwargs): print('hello world') return func(*args, **kwargs) return wrapper @say_hello # 相当于add = say_hello(add) def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res res1 = add(1, 2) print('res1:', res1) 
-
-
示例代码3:带参数的函数装饰器
-
# 带参数的装饰器 # say_hello 接收配置 # outer 接收函数 # wrapper 接收参数 def say_hello(msg): def outer(func): def wrapper(*args, **kwargs): print(f'hello world, 我要开始{msg}计算') return func(*args, **kwargs) return wrapper return outer @say_hello('加法') def add(x, y, z): res = x + y + z print(f'{x} + {y} + {z} = {res}') return res @say_hello('减法') def sub(x, y): res = x - y print(f'{x} - {y} = {res}') return res result1 = add(1, 2, 3) print('result1:', result1) result2 = sub(4, 5) print('result2:', result2) 
-
-
示例代码4:多个函数装饰器一起使用
-
# 多个装饰器一起使用 def test1(func): print('我是test1装饰器') def wrapper(*args, **kwargs): print('test1前面追加的逻辑') res = func(*args, **kwargs) print('test1后面追加的逻辑') return res return wrapper def test2(func): print('我是test2装饰器') def wrapper(*args, **kwargs): print('test2前面追加的逻辑') res = func(*args, **kwargs) print('test2后面追加的逻辑') return res return wrapper # 等价于add=test1(test2(add)) @test1 @test2 def add(x, y): result = x + y print(f'{x} + {y} = {result}') return result result1 = add(1, 2) print('result1:', result1) 
-
13.2 类装饰器
- 包含
__call__方法的类,就是类装饰器。 - 像调用函数一样,去调用类装饰器的实例对象,就会触发
__call__方法的调用。 __call__方法通常接收一个函数作为参数,并且会返回一个新函数。- 示例代码1:手动装饰
-
# 包含__call__方法的类,就是类装饰器。 # 像调用函数一样,去调用类装饰器的实例对象,就会触发__call__方法的调用。 # __call__方法通常接收一个函数作为参数,并且会返回一个新函数。 class SayHello: def __call__(self, func): def wrapper(*args, **kwargs): print('hello world') return func(*args, **kwargs) return wrapper def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res # 正常调用add函数 print('*********正常调用************') result1 = add(1, 2) print('result1:', result1) # 使用SayHello装饰add函数(手动装饰) print('*********手动装饰************') say1 = SayHello() add = say1(add) result2 = add(1, 2) print('result2:', result2) 
-
- 示例代码2:语法糖@
-
# 包含__call__方法的类,就是类装饰器。 # 像调用函数一样,去调用类装饰器的实例对象,就会触发__call__方法的调用。 # __call__方法通常接收一个函数作为参数,并且会返回一个新函数。 class SayHello: def __call__(self, func): def wrapper(*args, **kwargs): print('hello world') return func(*args, **kwargs) return wrapper # 使用@语法糖使用类装饰器 @SayHello() def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res result2 = add(1, 2) print('result2:', result2) 
-
- 示例代码3:带参数的类装饰器
-
class SayHello: def __init__(self, msg): self.msg = msg def __call__(self, func): def wrapper(*args, **kwargs): print(f'hello world, 我要开始{self.msg}') return func(*args, **kwargs) return wrapper # 使用@语法糖使用类装饰器 @SayHello('加法计算') def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res result2 = add(1, 2) print('result2:', result2) 
-
- 示例代码4:多个类装饰器一起使用
-
class Test1: def __call__(self, func): def wrapper(*args, **kwargs): print('Test1追加的逻辑') return func(*args, **kwargs) return wrapper class Test2: def __call__(self, func): def wrapper(*args, **kwargs): print('Test2追加的逻辑') return func(*args, **kwargs) return wrapper @Test1() @Test2() def add(x, y): res = x + y print(f'{x} + {y} = {res}') return res result = add(1, 2) print('result:', result) 
-
14 类型注解
14.1 变量类型注解
- 给变量加上类型说明,可增强代码的可读性,让IDE的提示更友好。
- 示例代码
-
# 变量类型注解: 给变量加上类型说明,可增强代码的可读性,让IDE的提示更友好。 num: int = 100 price: float = 12.5 message: str = 'hello world' is_vip: bool = True result: None = None # 语法没问题,但这样写没有意义 # 注意:可以先写变量的类型注解,以后再赋值 school: str school = '北京大学' # 容器类型注解 # hobby1是列表,并且列表中的所有元素必须是 str 类型 hobby1: list[str] = ['Python', 'Java', 'C++'] # hobby2是列表,列表中的所有元素可以是 str 或 int 类型 hobby2: list[str | int] = ['Python', 'Java', 100, 'C++', 520] # hobby3是列表,列表中的所有元素可以是 str 或 int 类型(旧写法) from typing import Union hobby3: list[Union[int, str]] = ['Python', 'Java', 'C++', 789] # city1是集合,并且集合中所有元素必须是str类型 city1: set[str] = {'北京', '上海', '广州'} # city2是集合,集合中元素必须可以是str、float、bool类型 city2: set[str | float | bool] = {'北京', True, 12.88} # person1是字典,键是str类型,值是int类型 person1: dict[str, int] = {'张三': 18, '李四': 20, '王五': 16} # person2是字典,键是str或int类型,值是int类型 person2: dict[str|int, int] = {'张三': 18, '李四': 20, '王五': 16, 12:22} # scores1是元组,并且元组中仅包含1个int类型的元素 scores1: tuple[int] = (10,) # scores2是元组,并且元组中仅包含3个int类型的元素 scores2: tuple[int, int, int] = (10, 20, 30) # scores3是元组,元组中可以包含任意个数的元素,但元素的类型必须是int scores3: tuple[int, ...] = (10, 20, 30, 40, 50, 60, 70, 80) # scores4是元组,元组中可以包含任意个数的元素,元素的类型可以是int或str scores4: tuple[int | str, ...] = (10, 20, 30, 'hello', 50, 'world', 70) # Python会根据初始赋值推导变量的类型 # 对于普通变量:后续如果改变类型,不会警告 # 对于容器变量:要求内部元素类型必须与推导出来的一致,否则就会警告 x = 199 print('x: ', x) x = 'hello world' # 不会警告 y = [10, 20, 30] print('y: ', y) y.append('40') # 会警告,但不会报错 print('y: ', y)
-
14.2 函数类型注解
- 函数类型注解:给函数的【参数】和【返回值】添加类型说明。
- 语法格式:函数名(参数1: 类型, 参数2: 类型) -> 返回值类型:
- 示例代码
-
# 函数类型注解:给函数的【参数】和【返回值】添加类型说明。 # 语法格式:函数名(参数1: 类型, 参数2: 类型) -> 返回值类型: # 示例1:设置参数类型注解、设置返回值类型注解 def add1(x: int, y: int) -> int: return x + y res1 = add1(19, 29) print('res1: ', res1) # 示例2:参数有默认值(Python可以推导处参数的类型) def add2(x=1, y=2) -> int: return x + y res2 = add2(20, 40) print('res2: ', res2) # 示例3:设置多个返回值的类型注解 def show_nums_info(nums: list[int]) -> tuple[int, int, float]: max_value = max(nums) min_value = min(nums) res = max_value / min_value return max_value, min_value, res res3 = show_nums_info([10, 20, 30, 40, 50, 60]) print('res3: ', res3) # 示例4:设置*args的类型注解,要求args中每个参数都必须是int类型 def add3(*args: int) -> int: return sum(args) res4 = add3(1, 2, 3, 6, 78, 22) print('res4: ', res4) # 示例5:设置**kwargs的类型注解,要求kwargs中每组参数的值,必须是str或int类型 def show_info(**kwargs: str | int): print(kwargs) show_info(name='张三', age=23) # 获取函数的注解信息 print(add1.__annotations__) 
-
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/new9232/article/details/167495588














