python基础(七)——函数
函数
概念
定义: 函数是带名字的代码块,用于完成具体的任务,可以重复使用。
作用: 当程序中需要多次执行同一项任务时,无需反复编写相同代码,只需调用函数即可。
优势: 通过使用函数,程序编写、阅读、测试和修复都更加容易。
执行顺序: Python中的函数必须先定义后使用,与JAVA不同,Python是解释执行的。
语法
def 函数名(参数列表):
函数体
[return]
命名规则: 函数名遵循小写蛇形命名法,与变量命名规则一致。
函数定义规则
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
示例
# 打印插入的字符
def print_sign(str):
print(str)
print_sign("123")
调用机制
执行流程: 程序从上往下执行,遇到函数定义不会立即执行,只有在调用时才会执行函数体内的代码。
调试技巧: 使用debug工具可以逐步查看函数执行过程,包括进入函数体(Step Into)和跳过函数体(Step Over)。
应用实例
问题: 相同功能的代码重复出现,导致代码冗长、维护困难。
例如:
print("床前明月光")
print("-------")
print("疑是地上霜")
print("-------")
函数优化
def print_sign():
for _ in range(10):
print("-", end="")
print()
print("床前明月光")
print_sign()
print("疑是地上霜")
print_sign()
函数的参数
参数的定义
参数概念:函数调用者与函数定义之间传递的数据
参数分类:
形参:函数定义时书写的参数(形式参数)
实参:函数调用时传入的参数(实际参数)
# 优化为用户自定义横线的数量
def print_sign(num): # 形参
for _ in range(num):
print("-", end="")
print()
print("床前明月光")
print_sign(10) # 实参
print("疑是地上霜")
print_sign(20)
# print_sign() # 如不传会报错print_sign() missing 1 required positional argument: 'num'
优势:通过参数提高函数灵活性,同一函数可实现不同效果
注意事项:定义了形参后必须传入对应个数的实参,否则运行报错
参数的默认值
设置方法:在形参后使用等号赋值,如def func(param=10)
调用规则:
不传参时使用默认值
传参时覆盖默认值
注意事项:无默认值的形参必须传入实参
# 优化为用户自定义横线的数量,如不传则默认打印10个
def print_sign(num=10): # 形参
for _ in range(num):
print("-", end="")
print()
print("床前明月光")
print_sign()
print("疑是地上霜")
print_sign(20)
参数类型
位置参数
第一个实参赋值给第一个形参,第二个实参赋值给第二个形参,严格按顺序匹配。
错误示例:若将字符串传给数值型参数,会导致逻辑错误或运行时异常。
关键字参数
通过参数名=值的形式显式指定传参,形参名必须与函数定义一致。
优势:允许颠倒参数顺序,例如func(b=2, a=1)。
限制:混合使用时,关键字参数必须位于位置参数之后,否则报错。
解包参数
若函数的形参是定长参数,可以通过 * 和 ** 对列表、元组、字典等解包传参。
数量必须匹配:容器元素数量需与形参数量严格一致,否则报错。
字典键名约束:**dict解包时,字典键必须与形参名完全对应。键名错误或数量不匹配均会导致运行时错误。
可变长参数
在形参前加星号(*)表示接收多个参数
接收的参数会以元组形式存储
如果可变长参数后还有参数,必须使用关键字参数传递
默认值参数规则:
不传参时使用默认值
传参时会覆盖默认值
# 优化为用户自定义横线的数量,如不传则默认打印10个
# 优化为用户自定义横线的数量,传入打印符号,如不传则默认打印10个
def print_sign(num=10, sign="-"):
for _ in range(num):
print(sign, end="")
print()
print("床前明月光")
print_sign(sign="-", num=10) # 关键字参数
print("疑是地上霜")
print_sign(20, "*")# 位置参数
# 解包参数
def func(a, b, c):
return a + b + c
tuple1 = (1, 2, 3)
print(func(*tuple1)) # 6
list1 = (4, 5, 6)
print(func(*list1)) # 15
dict1 = {"a": 1, "b": 2, "c": 3}
print(func(**dict1)) # 6
#可变长参数
def func1(a, *nums):
print(a, nums)
func1(1, 2, 3, 4, 5) # 1 (2, 3, 4, 5)
# 如果可变长参数之后还有参数,必须通过关键字参数的方式进行传递
def func2(a, *nums, b):
print(a, nums, b)
func2(1, 2, 3, 4, 5, 6, b=9) # 1 (2, 3, 4, 5, 6) 9
强制使用位置参数或关键字参数
可以通过特定符号强制规定参数传递方式
#/ 前的参数必须使用位置传参,* 后的参数必须用关键字传参。
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
f(1, 2, 3, d=4, e=5, f=6)
深拷贝和浅拷贝
直接赋值浅拷贝深拷贝区别
直接赋值:仅复制引用
浅拷贝:“换汤不换药”(外层新容器,内层旧对象)
深拷贝:“脱胎换骨”(完全独立的新对象树)
典型应用场景:
- 直接赋值:需要同步修改的场景
- 浅拷贝:外层结构变化需要独立,但内层数据可共享
- 深拷贝:需要完全隔离的复杂嵌套结构
python中严格来讲,是没有值传递和引用传递的说法,因为在python中所有的数据都属于对象,都属于引用传递。
在函数体内对参数的修改是否会影响原变量,完全取决于是可变类型还是不可变类型
import copy
"""
# 直接传递
本质:复制对象的地址值(如 list2 = list1)
特点:新旧变量指向同一内存区域
"""
# 传递的参数没有被改变
def func1(a):
a += 1
print("函数中a的值", a) # 11
b = 10
func1(b)
print("实参中b的值", b) # 10
# 传递的参数改变了
def func2(nums):
nums[0] += 1
print("函数中nums的参数值", nums) # [2, 2, 3]
list1 = [1, 2, 3]
func2(list1)
print(list1) # [2, 2, 3]
print("-" * 20)
"""
深拷贝 (copy.deepcopy()):
递归地复制对象及其包含的所有子对象。
新对象与原对象完全独立,修改任何层级的数据都不会影响原列表。
"""
def func3(nums):
list1 = copy.deepcopy(nums)
list1[3][0] = 0
print(list1)
list2 = [100, 200, 300, [3, 4, 6]]
func3(list2)
print(list2)
"""
浅拷贝
只复制对象本身(第一层)。
如果列表中包含可变对象(如嵌套列表、字典),新旧列表中的这些子对象仍然指向同一个内存地址。
修改嵌套对象会影响原列表。
"""
def func4(nums):
list1 = list.copy(nums)
list1[3][0] = 0
print(list1) # [100, 200, 300, [0, 4, 6]]
list2 = [100, 200, 300, [3, 4, 6]]
func4(list2)
print(list2) # [100, 200, 300, [0, 4, 6]]
return关键字
概念及作用
基本概念:函数通过return关键字将结果返回给调用者,返回到函数调用的位置
关键作用:实现函数调用前后逻辑的串联,使函数结果能在后续代码中被利用
语法特点:return表示结束函数并返回内容,具有双重功能
返回类型:可以返回任意数据类型(字符串、数字、布尔值、列表、元组等)
- 无return语句的函数默认返回None
- 接收无返回值函数的结果会得到None(可能引发类型错误)
def sum_fun(a, b):
return a + b
# 可以写对个return,但只会执行第一个
return a
return b
get_num = sum_fun(4, 2)
if get_num % 2 == 0:
print("偶数")
else:
print("奇数")
# 无返回值或不写返回值,默认返回None
def func2(a, b):
print(a + b)
return
num1 = func2(1, 2)
print(num1) # None
多级调用
在一个函数中调用另一个函数,当内层函数执行完之后才会继续执行外层函数。
def func_a():
print("函数a开始执行")
print("函数a执行中")
print("函数a结束执行")
def func_b():
print("函数b开始执行")
func_a()
print("函数b执行中")
print("函数b结束执行")
"""
函数b开始执行
函数a开始执行
函数a执行中
函数a结束执行
函数b执行中
函数b结束执行
"""
func_b()
递归
简单说就是函数自己调自己
递归的核心是函数自我调用,但需满足以下条件:
- 逻辑拆分:将大问题分解为若干小问题
- 终止条件:必须存在明确的递归出口
- 自我调用:通过函数调用当前函数实现问题分解
未设置终止条件导致无限调用,最终耗尽栈内存空间。
def func1():
func1()
# 报错RecursionError: maximum recursion depth exceede
func1()
# 求1~n之和 递归实现
def num_sum(nums):
# get_sum = 0
# if nums >= 1:
# get_sum = nums + num_sum(nums - 1)
get_sum = nums + num_sum(nums - 1) if nums > 1 else 1
return get_sum
print(num_sum(5)) # 15
匿名函数
Haskell介绍
纯函数式编程: Haskell是一种标准化的纯函数编程语言,具有非限定性语义和强静态类型特性
函数第一性: 在Haskell中"函数是第一类对象",主要控制结构就是函数
Lambda演算: 以λ演算为基础发展而来,使用希腊字母λ作为标志
历史渊源: 命名源自美国逻辑学家哈斯凯尔·加里,1990年在Miranda语言基础上标准化
匿名函数介绍
核心思想: 不关注函数名称,只关注函数功能(面向函数编程)
语法格式: lambda 参数列表: 表达式
与传统函数区别:
- 不需要def关键字定义
- 没有函数名
- 函数体只能是单个表达式
sum = lambda a, b: a + b
print(sum(10, 20)) # 30s
匿名函数作为内置函数的参数
from functools import reduce
# sorted
"""
将下列学生按学生年龄排序
"""
student_list = [
{"name": "张三", "age": 36},
{"name": "李四", "age": 14},
{"name": "王五", "age": 27},
]
print(sorted(student_list, key=lambda x: x["age"]))
print("-" * 20)
# map,对列表中的元素进行逐个处理
map_result = map(lambda x: x * x, [2, 3, 4, 5]) # 每个数与自身相乘
print(list(map_result)) # [4, 9, 16, 25]
print("-" * 20)
# filter 对元素进行过滤
filter_list = list(filter(lambda x: x % 2 == 0, [2, 3, 5, 6, 8, 9])) # 筛选出偶数
print(filter_list) # [2, 6, 8]
# reduce 累积操作
reduce_list = reduce(lambda x, y: x * y, [1, 2, 3, 4, 5, 6])
print(reduce_list)# 720
作用域
Python中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了哪一部分程序可以访问哪个变量,Python的作用域一共有4种,分别是:
- L (Local) 局部作用域
- 局部作用域变量仅在定义它的函数内部有效。
- E (Enclosing)嵌套作用域 闭包函数外的函数中
- 嵌套作用域变量存在于外层函数中,可被内层函数访问
- G (Global) 全局作用域
- 全局作用域变量在模块层级定义,全局有效
- B (Built-in) 内建作用域
- 内建作用域包含Python预定义的函数或变量(如print、len)。当其他作用域均未找到变量时,解释器会查找内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。以下案例演示各种作用域类型。
- 内建作用域包含Python预定义的函数或变量(如print、len)。当其他作用域均未找到变量时,解释器会查找内建作用域
# 全局作用域
a = 2
b = 0
def outer():
c = 2 # 嵌套作用域
def inner():
d = 6 # 局部作用域
print(a, b, c, d) # 2 0 2 6
# 由于没有在其他作用域找到对应的变量,所以在解释器内部找变量和函数,找到就返回
print(len) # <built-in function len>
# NameError: name 'llm' is not defined 所有都没找到则报错
# print(llm)
return inner
in_func = outer()
in_func()
global和nolocal关键字
glbal
- global关键字用于显式声明使用全局作用域的变量,而非默认作用域的变量
- 对于可变数据类型(如列表),可直接修改全局变量
- 对于不可变数据类型(如数值),需使用global关键字声明后才能修改全局变量
nonlocal
- nonlocal关键字用于声明使用嵌套函数作用域的变量
- 必须存在嵌套函数结构才能使用
- 不使用默认局部作用域,而使用外层嵌套函数作用域的变量
num = 100
def fun1():
# 显式说明使用全局作用域中的变量,而非局部作用域
global num
num = 200
print(num)
fun1()
print(num) # 200
print("-" * 20)
def fun2():
nums1 = 100
print(nums1) # 100
def fun3():
nonlocal nums1
nums1 = 200
print(nums1)
fun3() # 200
inner_fun = fun2
inner_fun()
更多推荐




所有评论(0)