函数

  • 函数用于封装一个特定的功能,表示 一个 功能或者行为。
  • 函数是可以重复执行的语句块, 可以重复调用。因此可以提高代码的可重用性和可维护性,使代码层次结构更清晰。
  • 函数最本质的思想是将程序的 ‘做’ 和 ‘用’ 拆分。解决了开发过程中 ‘做’ + 多次 ‘用’ 的场景。
  • 函数的设计理念:崇尚小而精,拒绝大而全,灵活大于全面。
1
2
3
4
5
6
7
8
9
def 函数名(形参1, 形参2):
函数体
retuen 返回值 # 可以没有返回值

# def 关键字:全称是define,意为”定义”。
# 函数名:对函数体中语句的描述,规则与变量名相同。
# 形参:函数定义者要求调用者提供的信息。
# 函数体:完成该功能的语句。
# 返回值:传递回的信息。
1
2
3
4
变量 = 函数名(实参)
print(变量) # 返回值如果没有明确,函数默认返回 None。

# 实参:给到形参的具体的数据。

函数注释

1
2
3
4
5
6
7
8
9
10
11
12
def 函数名(形参1: 形参1类型, 形参2: 形参2类型) -> 返回值类型:
"""
自定义函数文档
:param 形参1: 描述
:param 形参1: 描述
:return: 描述
"""
函数体
retuen 返回值

# 使用文档字符串描述函数的功能与参数。
# 可以添加形参和返回值的类型注释。

使用内存图理解函数

  • 不可变类型的数据传参时,函数内部不会改变原数据的值。
  • 可变类型的数据传参时,函数内部可以改变原数据。

补充

除了注释,函数的名字要起的合理,而且要使用动词命名,方便使用时理解函数。

程序的运行是自上而下的,有一个先后顺序,要先定义函数,然后再用。

对于 pycharm 而言,调试时使用快捷键 F8 step over 是不会进入到函数中的,F7 step into 是会进入的。换句话说,F7 会进入到代码的下一层,F8 会跳转到当前层的下一行。

python 的函数是没有重载的必要的,因为 python 并不会对输入的类型和数量进行判断。python 有自己的类似重载的办法。


函数参数

函数参数有实参和形参两个板块。然后有位置和关键字两种方式。

形参

约束/限制实际参数。

位置形参:实参必填。

1
2
3
4
5
def func01(p1, p2, p3):
print(p1, p2, p3)

# func01() # 报错
func01(1, 2, 3) # 1 2 3

星号元组形参:自动将多个实参合并为一个元组。只支持位置实参。

1
2
3
4
5
def func03(*args):  # 就使用 args 命名变量。星号修饰输入。
print(args)

func03() # tuple()
func03(1, 2, 3) # tuple(1,2,3)

默认形参:实参可选,每一个形参有一个默认值。
判定是否为默认形参的依据是是否有默认值。

1
2
3
4
5
6
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

双星号元组形参:自动将多个实参合并为一个元组。只支持关键字实参。

1
2
3
4
5
def func04(**kwargs):  # 就使用 kwargs 命名变量。双星号修饰输入。
print(kwargs)

func04() # dict()
func04(p1=1, p2=2) # {'p1': 1, 'p2': 2}

组合在一起

命名关键字形参:输入多个实参后,使用关键字传递参数。
限制函数输入形式,*args 后面的形参必须使用关键字传递参数。

1
2
3
4
def func05(*args, p3):
print(args, p3)

func05(1, 2, p3=3) # (1, 2) 0

简化上面的代码,可以只保留 * ,仅使用限制函数参数输入形式为关键字的功能。

1
2
3
4
5
def func06(p1, *, p3):
print(p1, p3)

# func06(1,3) # 报错
func06(1, p3=3) # 1 3

参数自左至右的顺序:仅供参考。

1
2
3
4
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 的第十种函数参数是 强制位置形参。限制函数输入形式,必须使用位置实参传递参数。


实参

如何与形参对应。

1
2
def func01(p1, p2, p3, p4):
print(p1, p2, p3, p4)

注:接下来的所有例子都会打印 1 2 3 4。

位置实参:实参与形参的位置依次对应。

1
func01(1, 2, 3, 4)

序列实参:将序列拆分后按顺序与形参进行对应。

1
2
itrable_in = 1, 2, 3, 4 # 传入的是序列中的元素。
func01(*itrable_in) # python的解释器在遇到星号时会告诉CPU接下来的变量内的元素是函数参数。

关键字实参:实参根据形参的名字进行对应。

1
2

func01(p2=2, p1=1, p4=4, p3=3)

字典实参:将字典拆分后按名称与形参进行对应。

1
2
dict_in = {'p1': 1, 'p2': 2, 'p3': 3, 'p4': 4}
func01(**dict_in)

组合在一起

先位置实参 -> 再关键字实参

1
func01(1, 2, p4=4, p3=3)

先序列实参 -> 再字典实参

1
2
3
itrable_in = 1, 2
dict_in = {'p3': 3, 'p4': 4}
func01(*itrable_in, **dict_in)

先位置 -> 再序列 -> 最后 (关键字 -> 字典) 或 (字典 -> 关键字)

1
2
3
4
itrable_in = (2,)
dict_in = {'p4': 4}
func01(1, *itrable_in, **dict_in, p3=3)
func01(1, *itrable_in, p3=3, **dict_in)

补充

注意:内置函数是不兼容关键字实参,一定要用位置实参。


变量作用域

作用域-LEGB 是变量起作用的范围。

  1. Local局部作用域:函数内部。
  2. Enclosing 外部嵌套作用域 :函数嵌套。
  3. Global全局作用域:模块(.py文件)内部。
  4. Builtin内置模块作用域:builtins.py文件。

变量名的查找由内到外:L -> E -> G -> B。在访问变量时,先查找本地变量,然后是包裹此函数外部的函数内部的变量,之后是全局变量,最后是内置变量。

Local 局部变量

定义在函数内部的变量(形参也是局部变量)。

局部变量只能在函数内部使用。调用函数时被创建,函数结束后自动销毁。

Global 全局变量

定义在函数外部,模块内部的变量。

在整个模块 py 文件范围内访问,但函数内不能将其直接创建赋值。

global 语句

在函数内部使用 global 创建全局变量,等同于在文件内创建全局变量。

在函数内部修改全局变量。因为在函数内直接为全局变量赋值/修改,视为创建新的局部变量。

当然通过索引定位到了全局变量中的元素再修改是可以的。

1
2
3
4
5
6
7
8
9
global 变量1, 变量2, …
变量1 = 赋值数据

# 错误示范
期望修改的全局变量1 = 数据 # python 认为创建了一个局部变量:变量1。

# 正确示范
global 期望修改的全局变量1 # 先告诉 python 这个变量在这个局部也是当做全局变量看。
期望修改的全局变量1 = 数据

注意:不能先声明局部的变量,再用global声明为全局变量。

nonlocal 语句

nonlocal 在内层函数修改外层嵌套函数内的变量。

1
nonlocal 变量名1,变量名2, ...

相较于 global,nonlocal 是一层一层得使用。


算法基础

1
2
# 变量交换
a,b=b,a
1
2
3
4
5
# 循环计数
开始、结束、间隔
0 +=1
1 *=数字
[] append
1
2
3
4
5
6
# 计算最值
max_value = list01[0]
for i in range(1,len(list01)):
if max_value < list01[i]:
max_value = list01[i]
print(max_value)
1
2
3
4
5
6
7
8
#  冒泡排序
# 时间 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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 计数排序
# 时间 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)