函数
是一组指令的集合 、由多个任务组成。函数 可以 让 代码 进行 复用,函数中的任务 是 相对独立的。
函数的定义
语法 : 在 python 中,韩式 使用 def 关键字 进行定义 、 函数名 是一个标识符
def 函数名( [参数列表] ):
函数体 (代码)
def my_sum(n):
sum = 0
for x in range(n):
sum += x
print(sum)
return 关键字
return 关键字作用 : 可以将 函数执行的结果 返回给 调用者、如果函数没有明确的 return , 则默认 return 一个 None
return 可以 结束整个函数的执行、所以在 return 后 的代码 不会执行。
在 Python 语言中, 一个函数 允许返回 多个结果、 Python 会将所有的结果 存储到 元组中 返回给 调用者
def my_sum(n):
"""
计算 0 ~ n 之间所有的整数 和 , 包含 0 不包含 n
:param n: 终止值
:return : 返回计算的所有数字和
"""
sum = 0
for x in range(n):
sum += x
return sum
函数对象 常见的属性
__doc__
: 获取 函数的 文档注释__name__
: 获取 函数的名字、返回的字符串
函数的参数
- 必传参数
def sum(a, b):
"""
计算两个整数的和
:param a:
:param b:
:return:
"""
return a + b
sum() # 报错、缺失2个参数
sum(1, ) # 报错, 缺失 1个参数
sum(1, 2) # 正常
sum(1, 2, 3) # 报错、需要 2个参数、但传递了3个
a 和 b 参数 在调用时候 ,都必须传递,否则 会产生错误
- 默认参数
def sum(a, b=0): """ 计算两个整数的和 :param a: :param b: :return: """ return a + b sum(1, 2) # a = 1, b = 2 sum(b=1, a=2) # a = 2, b = 1 sum(a=2) # a = 2, b = 0 sum(b=2) # 报错, 因为 a 是 必传参数
默认参数 的定义位置 必须 写在 必传参数的 后面 、 调用函数的时候 默认是 按照 参数定义的位置 依次传递数据、但也可以 通过 关键参数 给指定位置的参数 传值。
- 关键参数
如果要定义 关键参数、只需要 使用
*
来分割 其他类型的参数即可。def sum(a, *, b): """ 计算两个整数的和 :param a: :param b: :return: """ return a - b sum(1) # 报错 , b 是一个关键必传参数 sum(1, 2) # 报错 , b 是一个关键参数、必须在调用的时候 使用 关键字 进行调用。 sum(1, b=2) # 正确写法 sum(b=2, a=1) # 正确写法 sum(b=2, 1) # 报错、 第一个参数 使用关键参数传递、那么 后面的参数 都必须使用 关键参数 传递。
- 不定项参数
参数的个数 不确定。 在调用的时候, args 不能使用 关键参数 传递
*args
负责 接收 多个参数 。 args 是 元组 , 在调用的时候,如果传入的是 字符串、 列表、元组、集合 ,可以使用*
进行解包 。**kwargs
负责接收 多个关键参数。kwargs 是 字典 , 在调用的时候,如果 传入的是 字典, 可以 通过**
进行解包。def my_max(a, *args, **kwargs): max = a for x in args: if max < x: max = x return max x = my_max(1, 2, 3, 4, 5, 7, 11, 12, 34, 1 , x=1, y=2, z=3) print(x)
参数的传递
形参: 在 定义函数的时候, 参数列表中 定义的参数 被称为 形参
实参: 在 调用 函数的时候, 传入的数据
- 不可变类型 参数传递 (值传递): 在调用函数的时候,将 数据的值 传递给 函数
def exchange(a, b): a, b = b, a a = 3 b = 4 exchange(a, b) print(a, b) // 3, 4
- 可变类型 参数传递 (引用传递) : 在 调用函数的时候 ,将 数据的 地址 传递给 函数
def exchange(ls): ls[0], ls[1] = ls[1], ls[0] ls = [3, 4] exchange(ls) print(ls) // [4, 3]
匿名函数
是一个特殊的函数 、 特殊在 函数没有 名字 。
匿名使用 使用 关键字 lambda 来定义
语法:
lambda 参数列表: 函数体代码
参数 列表 可以有 0 ~ n 个, 多个参数使用 逗号 分割
函数体代码 有且只能有一条语句、如果需要返回结果 、可以省略 return
# 普通函数
def test(x):
return x + 3
# 匿名函数
lambda x: x + 3
递归
一个函数 在 函数体中 自己 调用 自己 、那么 这个现象 就称为 递归调用。
a) 编写 递归调用 必须 要非常清楚 函数的作用
b) 递归调用 必须 提供 收敛条件(结束递归的手段)Python 中 递归默认有深度限制,如果要修改该限制,可以通过
sys.setrecursionlimit(x)
更改递归的性能是非常差的,通常情况下不建议采用递归
def sum(n):
"""
求 前 n 项 连续的 数字 和
"""
if n == 1:
return 1
return n + sum(n - 1)
def fac(n):
"""获取斐波那契数列中 第 n 项的数字"""
if n == 1 or n == 2:
return 1
return fac(n - 1) + fac(n - 2)
函数作用域
循环/判断 的块 不会产生新的作用域, 而函数 / 类 对应的块 会产生 新的作用域
- 全局变量 : 在 模块中 定义的变量 是 全局变量。
- 局部变量: 在 函数中 定义的变量 是 局部变量。局部变量的作用范围是 整个函数,一旦出了函数,变量则失效。
- 非局部变量: 在函数嵌套中、站在内部函数的角度看 外部函数定义的变量 、被称为 非局部变量
作用域的查找顺序 是 局部 —> 非局部 —> 全局 ----> builtins (内建模块)
a = 10 # 全局变量
def test():
b = 20 # 非局部变量
def inner():
c = 30 # 局部变量
inner()
global 关键字
如果 在 函数中要使用 全局变量 、 可以使用 global 关键字 标记
a = 10
def test():
globa a # 标记 a 是一个 全局变量, 否则 a = a + 1 会 被 解析器 认为 a 是一个局部变量, 且没有定义的时候使用 a 而产生错误。
a = a + 1
print(a)
test()
nonlocal 关键字
如果 在 内部函数中 要 使用 外部函数 中定义的 变量, 可以使用 nonlocal 关键字 标记
def test():
a = 10
def inner():
nonlocal a # 标记 a 是一个 非局部变量, 否则 a +=1 会被 解析器 认为 a 是一个局部变量, 且没有定义的时候使用 a 而产生错误。
a += 1
print(a)
inner()
globals() 函数
用来 获取 全局 范围内所有的 变量 组成的 字典
a = 1
b = 2
print(globals()) # {.... "a":1, "b":2}
locals() 函数
用来获取 局部 范围内所有的 变量 组成的 字典
def test():
a = 1
b = 2
print(locals()) # {"a": 1, "b": 2}
闭包 & 函数嵌套
闭包 是一种 特殊 的 函数 嵌套 。 外部函数 返回 内部函数 的 引用对象。
闭包 可以 延长 外部函数中定义的 变量的 作用范围 。在 Python 中 闭包 还可以作为
装饰器
来使用闭包的缺点 : 滥用闭包 有可能会导致产生 内存溢出的风险 。
def outer():
"""
外部函数 主要用来将 变量 a 保护起来,不让别的程序随意访问
"""
a = 0
def generator_id():
"""内部函数负责 生成唯一且连续的数字"""
nonlocal a
a += 1
return a
# 外部函数必须返回内部函数的引用对象, 如果添加了 括号进行调用,则不再构成闭包。
return generator_id
# 调用 outter 返回内部函数的引用对象
x = outer()
# 因为 内部函数在内存在被赋值给了x, 所以 和 内部函数相关的数据都会被保留, 从而让 外部函数中的 a 作用范围延长,没有再 outer调用结束后销毁。
print(x())
print(x())
print(x())
装饰器 decrator
装饰器的本身是采用闭包技术 实现的。
装饰器 可以在 不改变 函数 / 类 的 前提下 , 对 功能 进行 增强
装饰 函数
- 外层 函数 的参数 为 要装饰 的 目标 函数对象 、 外层 函数 返回 内层函数 的 引用对象 。
- 内层 函数 的参数 为 要装饰 的 目标函数的 参数列表, 通常用
*args
和**kwargs
表示 - 内层 函数 一般情况下 做 三件事
- 处理 装饰 逻辑
- 调用 目标函数 、并获取函数的结果
- 返回 目标 函数的 执行结果
def logs(fn):
def inner(*args, **kwargs):
# 调用目标函数
ret = fn(*args, **kwargs)
# 装饰器中主要负责 记录 函数的执行日志
print("正在执行" + fn.__name__ + "函数、参数是", args, kwargs, "返回值是", ret, sep="")
return ret
return inner
使用 装饰器
@logs
def sum(a, b):
return a + b
在 函数的上方使用
@
符号 加 装饰函数的名字 、就可以让 函数 引入 装饰器 , 当调用函数的时候,会自动执行 装饰器中的 代码, 由 装饰器内部函数中的代码 完成 目标函数的调用, 从而实现函数的增强效果。
调用 带有装饰器的目标函数
x = sum(1, 3) # sum 函数 已经被装饰器修饰、那么 sum函数 本质上已经发生改变,其实调用的是 装饰器中的 inner 函数
print(x)
# 上述代码 本质上是 、 此时 sum函数上不允许使用 @logs
logs(sum)(1, 3)
保留目标函数的原始信息
import functools
def logs(fn):
@functools.wraps(fn)
def inner(*args, **kwargs):
# 调用目标函数
ret = fn(*args, **kwargs)
# 装饰器中主要负责 记录 函数的执行日志
print("正在执行" + fn.__name__ + "函数、参数是", args, kwargs, "返回值是", ret, sep="")
return ret
return inner
装饰器的作用
- 日志 记录
- 性能分析
- 监控
- 权限检查
- 事务管理 (数据库)
- 处理返回值格式
带参数的装饰器
带参数的装饰器 需要 三层函数嵌套
第一层函数 的参数 代表 装饰器所需要的参数
第二层函数的参数 代表 装饰器 装饰的目标对象
第三层函数的参数代表 装饰器 装饰的目标对象的 参数列表
def ticket(action):
def decorator(func):
def inner(*args, **kwargs):
# 处理装饰器逻辑
# 调用目标函数
ret = func(*args, **kwargs)
# 处理装饰器逻辑
# 返回目标函数的结果
return ret
return inner
return decorator
@ticket(action="我是装饰器的参数")
def test():
print("我是目标函数")
生成器
- 可迭代对象 : 字符串、 元组、列表、集合 、字典
- 迭代器
在 python 中 ,可以 使用
iter
函数将可迭代对象 转换成 迭代器。 可以使用next
函数 来 每次 获取 迭代器中的 一个数据。当 将 迭代器中的所有数据 获取完成后, 再次 调用 next 会产生一个 StopIteration 错误。
迭代器 是一次性的 、数据的获取顺序 是从 第一个 到 最后一个 、 且过程 不可逆。
- 生成器
生成器 是一种 特殊的 迭代器 、通过某种算法 可以将大量数据 存储到内存在 ,且不占用太多的内存空间。通过计算存储的数据。生成器 可以表示无限个数据
生成器的实现方式
- 元组生成推导式
s = [12, 34, 65, 78, 23, 5]
# 将 列表中的数据 用 生成器来表示
data = (x for x in s)
- 在 函数中 、使用
yield
关键字 返回数据
def get_number():
i = 1
while True:
yield i
i += 1
# 一个函数 如果 使用了 yield , 程序会将 函数理解为 一个生成器对象、不会执行函数中的任何代码
ls = get_number()
# 执行函数中的代码,当 执行到 yield i 时, 将 i 的值 返回, 并暂停函数的执行,等待下一次 调用 next函数。
next(ls)
# 继续从 上一次暂停的位置 i+=1 开始执行、当执行到 yield 时,再次返回 i 的值,并暂停函数的执行。
next(ls)
内置函数
- abs(x) : 获取 x 数字的 绝对值
- all(iterable) : 判断 可迭代 对象中的 所有元素 是否 都 是 True (包含 隐式转换) 、 如果 可迭代对象 是空 的, 也返回 True
- any(iterable) : 判断 可迭代 对象中的 任意一个元素 是否 是 True (包含 隐式转换) 、如果 可迭代对象是 空的, 则 返回 False
- bin / oct / hex / int : 进制转换函数
- chr / ord : 字符 和 码点 的相互转换
- divmod(x, y) : 计算 x// y 和 x % y 、返回一个 长度为 2 的元组
- hash(x) : 计算 一个 不可变类型 对应的 hash 值
- isinstance(obj , type_tuple) : 判断 一个 对象是否 是 指定的 类型, 类型 如果有多个,则 使用 元组 。
- iter(iterable) : 将 一个 可迭代对象 转换成 迭代器
- len(seqence) : 获取 序列的 长度
- max/min(*args) : 获取传入数据的最大/小值
- pow(x, y) : 计算 x 的 y 次幂
- round(x, n=None) : 将 x 进行 四舍五入, 默认取整 、 如果需要 保留 小数,可以 传入一个 n , n >0 即可。
- sum(iterable) : 将一个可迭代对象中的元素 累加求和
- sorted() : 排序 、返回一个 排序后的列表
lst = [
{"name": "李四", "age": 18},
{"name": "张三", "age": 18},
{"name": "王五", "age": 17},
{"name": "赵六", "age": 31},
]
# 按照 年龄 升序排列 、 如果年龄相同, 按照 名字 升序排列
x = sorted(lst, key=lambda y: (y["age"], y["name"]))
# 对 列表 去重 、还要保留 原 数据的顺序
ls = [1, 32, 54, 1, 34, 65, 1, 32]
print(sorted(set(ls), key=lambda y: ls.index(y)))
# 随机打乱
ls = (12, 34, 65, 23, 6, 1, 45)
new_ls = sorted(ls, key=lambda x: random.random())