Python 笔记第三部分:函数
函数
- 函数用于封装一个特定的功能,表示 一个 功能或者行为。
- 函数是可以重复执行的语句块, 可以重复调用。因此可以提高代码的可重用性和可维护性,使代码层次结构更清晰。
- 函数最本质的思想是将程序的 ‘做’ 和 ‘用’ 拆分。解决了开发过程中 ‘做’ + 多次 ‘用’ 的场景。
- 函数的设计理念:崇尚小而精,拒绝大而全,灵活大于全面。
def 函数名(形参1, 形参2):
函数体
retuen 返回值 # 可以没有返回值
# def 关键字:全称是define,意为”定义”。
# 函数名:对函数体中语句的描述,规则与变量名相同。
# 形参:函数定义者要求调用者提供的信息。
# 函数体:完成该功能的语句。
# 返回值:传递回的信息。
变量 = 函数名(实参)
print(变量) # 返回值如果没有明确,函数默认返回 None。
# 实参:给到形参的具体的数据。
函数注释
def 函数名(形参1: 形参1类型, 形参2: 形参2类型) -> 返回值类型:
"""
自定义函数文档
:param 形参1: 描述
:param 形参1: 描述
:return: 描述
"""
函数体
retuen 返回值
# 使用文档字符串描述函数的功能与参数。
# 可以添加形参和返回值的类型注释。
使用内存图理解函数
- 不可变类型的数据传参时,函数内部不会改变原数据的值。
- 可变类型的数据传参时,函数内部可以改变原数据。
补充
除了注释,函数的名字要起的合理,而且要使用动词命名,方便使用时理解函数。
程序的运行是自上而下的,有一个先后顺序,要先定义函数,然后再用。
对于 pycharm 而言,调试时使用快捷键 F8 step over 是不会进入到函数中的,F7 step into 是会进入的。换句话说,F7 会进入到代码的下一层,F8 会跳转到当前层的下一行。
python 的函数是没有重载的必要的,因为 python 并不会对输入的类型和数量进行判断。python 有自己的类似重载的办法。
函数参数
函数参数有实参和形参两个板块。然后有位置和关键字两种方式。
形参
约束/限制实际参数。
位置形参:实参必填。
def func01(p1, p2, p3): print(p1, p2, p3) # func01() # 报错 func01(1, 2, 3) # 1 2 3
星号元组形参:自动将多个实参合并为一个元组。只支持位置实参。
def func03(*args): # 就使用 args 命名变量。星号修饰输入。 print(args) func03() # tuple() func03(1, 2, 3) # tuple(1,2,3)
默认形参:实参可选,每一个形参有一个默认值。
判定是否为默认形参的依据是是否有默认值。def func02(p1=1, p2=2, p3=3): print(p1, p2, p3) func02() # 1 2 3 func02(0) # 0 2 3 func02(p2=0) # 1 0 3
双星号元组形参:自动将多个实参合并为一个元组。只支持关键字实参。
def func04(**kwargs): # 就使用 kwargs 命名变量。双星号修饰输入。 print(kwargs) func04() # dict() func04(p1=1, p2=2) # {'p1': 1, 'p2': 2}
组合在一起
命名关键字形参:输入多个实参后,使用关键字传递参数。
限制函数输入形式,*args 后面的形参必须使用关键字传递参数。def func05(*args, p3): print(args, p3) func05(1, 2, p3=3) # (1, 2) 0
简化上面的代码,可以只保留 * ,仅使用限制函数参数输入形式为关键字的功能。
def func06(p1, *, p3): print(p1, p3) # func06(1,3) # 报错 func06(1, p3=3) # 1 3
参数自左至右的顺序:仅供参考。def func07(p1, *args, p3=3, p4, **kwargs): print(p1, args, p4, p3, kwargs) func07(1, 2, 2, p4=4, p5=5) # 1 (2, 2) 4 3 {'p5': 5}
补充
python 的第十种函数参数是 强制位置形参。限制函数输入形式,必须使用位置实参传递参数。
实参
如何与形参对应。
def func01(p1, p2, p3, p4):
print(p1, p2, p3, p4)
注:接下来的所有例子都会打印 1 2 3 4。
位置实参:实参与形参的位置依次对应。
func01(1, 2, 3, 4)
序列实参:将序列拆分后按顺序与形参进行对应。
itrable_in = 1, 2, 3, 4 # 传入的是序列中的元素。 func01(*itrable_in) # python的解释器在遇到星号时会告诉CPU接下来的变量内的元素是函数参数。
关键字实参:实参根据形参的名字进行对应。
func01(p2=2, p1=1, p4=4, p3=3)
字典实参:将字典拆分后按名称与形参进行对应。
dict_in = {'p1': 1, 'p2': 2, 'p3': 3, 'p4': 4} func01(**dict_in)
组合在一起
先位置实参 -> 再关键字实参
func01(1, 2, p4=4, p3=3)
先序列实参 -> 再字典实参
itrable_in = 1, 2 dict_in = {'p3': 3, 'p4': 4} func01(*itrable_in, **dict_in)
先位置 -> 再序列 -> 最后 (关键字 -> 字典) 或 (字典 -> 关键字)
itrable_in = (2,) dict_in = {'p4': 4} func01(1, *itrable_in, **dict_in, p3=3) func01(1, *itrable_in, p3=3, **dict_in)
补充
注意:内置函数是不兼容关键字实参,一定要用位置实参。
变量作用域
作用域-LEGB 是变量起作用的范围。
- Local局部作用域:函数内部。
- Enclosing 外部嵌套作用域 :函数嵌套。
- Global全局作用域:模块(.py文件)内部。
- Builtin内置模块作用域:builtins.py文件。
变量名的查找由内到外:L -> E -> G -> B。在访问变量时,先查找本地变量,然后是包裹此函数外部的函数内部的变量,之后是全局变量,最后是内置变量。
Local 局部变量
定义在函数内部的变量(形参也是局部变量)。
局部变量只能在函数内部使用。调用函数时被创建,函数结束后自动销毁。
Global 全局变量
定义在函数外部,模块内部的变量。
在整个模块 py 文件范围内访问,但函数内不能将其直接创建赋值。
global 语句
在函数内部使用 global 创建全局变量,等同于在文件内创建全局变量。
在函数内部修改全局变量。因为在函数内直接为全局变量赋值/修改,视为创建新的局部变量。
当然通过索引定位到了全局变量中的元素再修改是可以的。
global 变量1, 变量2, …
变量1 = 赋值数据
# 错误示范
期望修改的全局变量1 = 数据 # python 认为创建了一个局部变量:变量1。
# 正确示范
global 期望修改的全局变量1 # 先告诉 python 这个变量在这个局部也是当做全局变量看。
期望修改的全局变量1 = 数据
注意:不能先声明局部的变量,再用global声明为全局变量。
nonlocal 语句
nonlocal 在内层函数修改外层嵌套函数内的变量。
nonlocal 变量名1,变量名2, ...
相较于 global,nonlocal 是一层一层得使用。
算法基础
# 变量交换
a,b=b,a
# 循环计数
开始、结束、间隔
0 +=1
1 *=数字
[] append
# 计算最值
max_value = list01[0]
for i in range(1,len(list01)):
if max_value < list01[i]:
max_value = list01[i]
print(max_value)
# 冒泡排序
# 时间 l**2, 内存 l, 通用排序算法.
list_s = [0, 4, 5, 3]
for i in range(len(list_s)): # l
for j in range(len(list_s)): # l
if list_s[i] < list_s[j]:
list_s[i], list_s[j] = list_s[j], list_s[i]
print(list_s)
# 计数排序
# 时间 l+l+l, 内存 l+l+w
# 不可以有重复元素, 不可以是小数, 适用于序号排序
# 假设数据的大小和数据的规模呈线性关系.
list_s = [0, 4, 5, 3]
memory_size = max(list_s) # l
list_result = [0 for i in range(memory_size)]
for item in list_s: # l
list_result[item] = 1
list_r = []
for i in range(memory_size): # l
if list_result[i]:
list_r.append(i)
print(list_r)