前面相关文章:
Python核心基础DAY1--Python的基础变量类型之字符串和数字类型
Python核心基础DAY2--复合类型之序列类型、映射类型和集合类型
Python核心基础DAY3--Python表达式及常见语句
Python函数
函数是可以重复执行的语句块,可以重复调用,用于封装语句块,提高代码的重用性。
是面向过程编程的最小单位
函数的定义:使用def 语句定义一个函数
def function_name(parameters):
# 函数体
# 进行一些操作
return result # 可选的返回值
- def:用于声明一个函数,告诉 Python 这是一个函数的定义。
- function_name:函数名,一个有效的标识符,规则和变量名一致。
- parameters:形参,可以是0 ~ n 个,参数之间用逗号分隔。
- 函数体:定义函数执行的具体操作。
- return:指定函数的返回值,没有则返回None。
一、内置函数
Python提供了大量的可直接使用的内置函数,主要执行一些常见的操作:数据处理、类型转换、数学计算、输入输出等。
all() -- 如果可迭代对象中的所有元素都为 True,返回 True,否则返回 False
print(all([True, True, False])) # 输出: False
print(all([1, 2, 3])) # 输出: True
sum() -- 返回可迭代对象中所有元素的总和
print(sum([1, 2, 3])) # 输出: 6
sorted() -- 返回一个新列表,其中包含可迭代对象中的元素,按照升序排序
print(sorted([3, 1, 2])) # 输出: [1, 2, 3]
reversed() -- 返回一个反向迭代器
print(list(reversed([1, 2, 3]))) # 输出: [3, 2, 1]
callable() -- 检查对象是否可以被调用(即是否是函数或方法)
print(callable(print)) # 输出: True
print(callable(123)) # 输出: False
zip() -- 将多个可迭代对象打包成一个元组,常用于并行遍历多个序列
names = ["Alice", "Bob"]
ages = [25, 30]
zipped = zip(names, ages)
print(list(zipped)) # 输出: [('Alice', 25), ('Bob', 30)]
eval() -- 将字符串作为有效的 Python 表达式来执行,并返回结果
x = 10
result = eval("x + 5")
print(result) # 输出: 15
exec() -- 执行存储在字符串中的 Python 代码
code = 'for i in range(3): print(i)'
exec(code)
# 输出:
# 0
# 1
# 2
globals() 和 locals() -- globals() 返回当前全局符号表(一个字典);locals() 返回当前局部符号表(也是字典)
x = 10
print(globals()) # 输出: 包含全局变量的信息
print(locals()) # 输出: 包含局部变量的信息
filter(function, iterable) -- 从可迭代对象中过滤出符合条件的元素
- 接受一个函数和一个可迭代对象
- 用接受的函数来筛选出可迭代对象中满足条件的元素
- 返回一个包含满足条件的元素的迭代器
numbers = [1, 2, 3, 4, 5]
filtered = filter(lambda x: x % 2 == 0, numbers)
print(list(filtered)) # 输出: [2, 4]
练习1
import math
def maxfn(data):
# 数据类型:list tuple set
assert isinstance(data, (list, tuple, set)),TypeError('数据类型错误')
max = -math.inf # 负无穷
for i in data:
if not isinstance(i, (int, float)): # 判断数据类型
raise TypeError('数据类型错误')
if i > max:
max = i
return max
print(maxfn([11,22,32,41,15]))
print('-'*50)
print(maxfn([1,2,'nihao']))
练习2
import random
with open('./students.txt',mode='r',encoding='utf-8')as f:
# 读取整个文件内容
content = f.read()
# 切割为列表
students = content.splitlines()
print(students)
# 读取已经被选中的学生
with open('./天选.txt',mode='r',encoding='utf-8')as f1:
stus = f1.read().splitlines()
# 智能选择没有选中的学生
vailstus = list(set(students).difference(set(stus)))
# 随机一个学生的下标
ind = random.randint(0,len(vailstus)-1)
# 根据随机的下标获取学生
student = vailstus[ind]
# 抽中的学生需要写入新的文件:记录下来
with open('./天选.txt','a',encoding='utf-8') as f2:
f2.write(vailstus[ind]+'\n')
二、函数调用
函数调用时传递参数的方式有多种,包括位置传参、关键词传参、多个参数解包、参数默认值等。
位置传参 — 最常见的传参方式,参数按定义的顺序依次传入函数
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
greet("Alice", 30) # 输出: Hello, Alice. You are 30 years old.
关键词传参 — 通过指定参数的名称来传值,无顺序限制,代码可读性较高
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
greet(age=30, name="Alice") # 输出: Hello, Alice. You are 30 years old.
参数默认值
定义函数时可为某些参数指定默认值,如果不传参则使用默认值。默认值参数必须位于无默认值参数的后面。
def greet(name, age=25):
print(f"Hello, {name}. You are {age} years old.")
greet("Alice") # 输出: Hello, Alice. You are 25 years old.
greet("Bob", 30) # 输出: Hello, Bob. You are 30 years old.
可变位置参数
使用 *args 可让函数接受任意数量的位置参数。*args 会将多余的位置参数收集成一个元组。
def sum_numbers(*args):
return sum(args)
print(sum_numbers(1, 2, 3)) # 输出: 6
print(sum_numbers(1, 2, 3, 4)) # 输出: 10
可变关键词参数
使用 **kwargs 可以让函数接受任意数量的关键词参数。**kwargs 会将多余的关键词参数收集成一个字典。
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=30)
# 输出:
# name: Alice
# age: 30
多参数解包
Python 允许在调用函数时解包序列或字典,使其作为位置参数或关键词参数传递给函数。
解包位置参数 — 使用 * 解包
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
args = ["Alice", 30]
greet(*args) # 输出: Hello, Alice. You are 30 years old.
解包关键词参数 — 使用 ** 解包
def greet(name, age):
print(f"Hello, {name}. You are {age} years old.")
kwargs = {"name": "Alice", "age": 30}
greet(**kwargs) # 输出: Hello, Alice. You are 30 years old.
参数混合使用
* | ** | 备注 | |
---|---|---|---|
可变参数 | 位置参数 | 关键词参数 | 用在形参前面 |
参数解包 | 位置参数解包,比如list、tuple | 关键词参数解包,比如dict | 用在实参前面 |
在函数调用时,可以混合使用位置参数和关键词参数,但位置参数必须放在关键词参数前面。
def greet(name, age, city="Unknown"):
print(f"Hello, {name}. You are {age} and live in {city}.")
greet("Alice", 30) # Hello, Alice. You are 30 and live in Unknown.
greet("Bob", 25, city="New York") # Hello, Bob. You are 25 and live in New York.
三、可变和不可变参数
在 Python 中,实参可以是可变类型或不可变类型。它们的区别主要体现在值传递和引用传递的行为上。
不可变类型
- 不可变类型包括:int、float、str、tuple、frozensetint、float、str、tuple、frozensetint、float、str、tuple、frozenset 等。
- 传递方式是值传递: 传递给函数的是该对象的值,函数内部修改该值不会影响外部变量的值。
def modify(x):
print('修改之前:', x, id(x))
x = 10 # 修改了 x 的值,但不会影响外部变量
print('修改之后:',x, id(x))
a = 5
modify(a)
print('原始数据:',a, id(a))
修改之前: 5 140722275297848
修改之后: 10 140722275298008
原始数据: 5 140722275297848
可变类型
- 可变类型包括:list、dict、setlist、dict、setlist、dict、set 等。这些类型的对象可以在原地修改。
- 传递方式是引用传递 :传递给函数的是对象的引用(即内存地址),在函数内部修改该参数的内容会直接影响外部变量。
def modify(lst):
print('修改之前:', lst, id(lst))
lst.append(4) # 修改了 lst 对象的内容
print('修改之后:',lst, id(lst))
a = [1, 2, 3]
modify(a)
print('外部原始数据:',a, id(a))
修改之前: [1, 2, 3] 2123053058816
修改之后: [1, 2, 3, 4] 2123053058816
外部原始数据: [1, 2, 3, 4] 2123053058816
四、匿名函数lambda
匿名函数是没有名字的函数,通常用于需要一个简短的、临时的函数场景,它可以有任意数量的参数,但只能包含一个表达式,并返回该表达式的结果。
基本语法:
lambda arguments: expression
# arguments:一个或多个输入参数,可以是位置参数或关键词参数。
# expression:一个单一的表达式,它的值将作为返回值返回。
匿名函数定义
# 定义一个 lambda 函数,接收两个参数 a 和 b,返回它们的和
add = lambda a, b: a + b
print(add(3, 5)) # 输出: 8
使用场景
lambda 函数常常与高阶函数如 map()、filter() 和 sorted() 等一起使用。
处理列表
numbers = [1, 2, 3, 4, 5]
# 使用 map() 将每个数字平方
squared_numbers = list(map(lambda x: x ** 2, numbers))
print(squared_numbers) # 输出: [1, 4, 9, 16, 25]
筛选列表
numbers = [1, 2, 3, 4, 5, 6]
# 使用 filter() 筛选出偶数
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # 输出: [2, 4, 6]
实现排序
# 按照字典的值排序
dict_list = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}, {"name": "Charlie", "age": 35}]
# 按照 age 排序
sorted_dict_list = sorted(dict_list, key=lambda x: x["age"])
print(sorted_dict_list)
# 输出: [{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]
条件表达式
可以在 lambda 函数中使用条件表达式(if-else)来做一些简单的决策
# 判断是否为偶数
is_even = lambda x: "Even" if x % 2 == 0 else "Odd"
print(is_even(4)) # 输出: Even
print(is_even(7)) # 输出: Odd
匿名函数优点
- 结构简单:
lambda
函数通常用在需要一个短小函数的地方,而普通函数则更适合复杂的逻辑。 - 匿名性:
lambda
函数是没有名字的,它们通常只在一个地方使用,并且不需要被重复调用。 - 功能限制:
lambda
函数只能包含一个表达式,不可以包含多行语句,而普通函数可以包含多行代码、条件判断、循环等复杂逻辑。
五、高阶函数
高阶函数是指可以接受一个或多个函数作为参数,或者返回一个函数作为结果的函数。
map — map(function函数, iterable可迭代对象)
- 接受一个函数和一个可迭代对象
- 将接受的函数应用到可迭代对象的每个元素上
- 返回一个包含结果的迭代器
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
# squared现在包含[1, 4, 9, 16, 25]
filter函数 — filter(function, iterable)
- 接受一个函数和一个可迭代对象
- 用接受的函数来筛选出可迭代对象中满足条件的元素
- 返回一个包含满足条件的元素的迭代器
numbers = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
# even_numbers现在包含[2, 4]
reduce函数 — reduce(function函数, iterable[, initializer]可迭代对象)
- reduce函数接受一个函数和一个可迭代对象
- 将接受的函数累积地应用到可迭代对象的元素上
- 可选的 initializerinitializerinitializer 参数可以作为累积的初始值
from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
# product现在包含120 (1 * 2 * 3 * 4 * 5)
作为参数:高阶函数可以接受其他函数作为参数,用于自定义行为。
def sort_custom_key(item):
return item[1]
data = [('apple', 3), ('banana', 1), ('cherry', 2)]
sorted_data = sorted(data, key=sort_custom_key)
# sorted_data现在包含[('banana', 1), ('cherry', 2), ('apple', 3)]
fn_oper = {
"add": lambda a, b: a + b,
"sub": lambda a, b: a - b,
"mul": lambda a, b: a * b,
"div": lambda a, b: a / b if b != 0 else None,
}
def dowhat(a, b, fn):
if not callable(fn):
assert fn in fn_oper, "无效操作"
fn = fn_oper[fn]
return fn(a, b)
print(dowhat(100, 200, fn_oper["add"]))
print(dowhat(100, 200, "add"))
print(dowhat(100, 200, "add1"))
六、变量作用域
变量的作用域是指在程序中某个变量的有效范围,也就是在代码的哪个部分可以访问或修改该变量。作用域定义了变量的可见性和生命周期。
变量作用域遵循LEGB规则(Local, Enclosing, Global, Built-in),依次搜索变量的定义位置。
Local→局部作用域
- 指函数或方法内部定义的变量。
- 仅在函数内部有效,函数外部无法访问。
- 在函数调用时被创建,在函数调用后自动销毁。
Enclosing→嵌套作用域
- 指外层函数中的变量,在内层函数中可访问,但不可修改。
- 当一个函数嵌套在另一个函数内部时,外层函数的变量属于Enclosing作用域。
def outer():
x = 20 # 外层函数变量
def inner():
print(x) # 访问外层函数变量
inner()
nonlocal:将局部作用域中变量声明为外部嵌套函数作用域中的变量
#外部嵌套作用域
def func01():
a = 10
def func02():
# 内部函数,如果修改外部嵌套变量,需要使用nonlocal语句声明
nonlocal a
a += 20
func02()
print(a)
func01()
Global→全局作用域
- 指模块级别定义的变量,整个模块都可以访问。
- 如果想在函数中修改全局变量,需要使用
global
关键字。
count = 0
def hello(name):
global count # 声明 count 是全局变量
print('hello', name)
count += 1
hello('小张')
hello('小李')
hello('小魏')
print('您共调用hello函数', count, '次')
Built-in→内建作用域
- 包含Python内建的函数、异常和常量,如
print()
,len()
,int
,Exception
等。 - 这些变量可以在任何地方使用。
print(len([1, 2, 3])) # 使用内建函数len
变量查找顺序 — 作用域遵循LEGB规则(Local, Enclosing, Global, Built-in):
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # 输出局部变量 'local'
inner()
outer()
如果不使用 global 关键字,函数内修改的会是局部变量,而不是全局变量。
七、函数内存分配
- 将函数的代码存储到代码区,函数体中的代码不执行。
- 调用函数时,在内存中开辟空间(栈帧),存储函数内部定义的变量。
- 函数调用后,栈帧立即被释放。
def func(a, b):
a = 20
b[0] = 20
a = 10
b = [100]
func(a, b)
print(a) # 10
print(b) # [20]
八、递归函数
递归是指函数直接或间接调用自身的过程。递归通常用于解决分解成相似子问题的复杂问题。
递归函数通常由两个部分组成:
- 终止条件:用于确保递归能够停止,避免无限递归。
- 递归调用:函数在其内部调用自身,传递简化或减少的参数,以解决更小的子问题。
阶乘定义
-
n! = n * (n-1) * (n-2) * ... * 1
- 递归公式:
n! = n * (n-1)!
,终止条件:0! = 1
:
def factorial(n):
# 终止条件:n为0或1时,返回1
if n == 0 or n == 1:
return 1
# 递归调用:n * (n-1)!
return n * factorial(n - 1)
# 测试
print(factorial(6)) # 输出 720
斐波那契数列
def fb(n):
if n == 0 or n == 1:
return n
return fb(n - 1) + fb(n - 2)
九、总结
Python中的语句是程序执行的最小单位,涵盖多种类型以实现不同功能。赋值语句用于给变量赋值;条件语句包括if-elif-else和Python 3.10后新增的match-case,前者根据条件执行不同代码块,后者适合多条件判断,支持多值匹配和嵌套if判断。循环语句有for循环和while循环,for循环用于遍历列表、元组等可迭代对象,while循环在条件为真时重复执行,且支持嵌套;range函数可生成整数序列辅助循环,循环中可通过break跳出整个循环、continue跳过当前循环,pass作为占位符不执行操作。字典遍历可通过键、值或键值对进行。异常处理语句包括try-except-finally捕获处理错误,raise主动抛出异常,assert用于断言检查,确保程序在错误发生时能合理响应。
函数是封装可重复执行语句块的工具,通过def定义,包含参数、函数体和可选返回值。Python提供丰富内置函数,如all、sum、sorted等,用于数据处理、类型转换等常见操作。函数调用方式多样,包括位置传参、关键词传参,支持参数默认值、可变位置参数(*args)和可变关键词参数(**kwargs),还可通过*和**解包序列或字典作为参数。参数传递中,不可变类型(如int、str)采用值传递,函数内修改不影响外部;可变类型(如list、dict)采用引用传递,修改会影响外部变量。
匿名函数lambda简洁高效,仅含一个表达式,常作为参数传给高阶函数如map、filter、sorted。高阶函数可接受或返回函数,map将函数应用于可迭代对象每个元素,filter筛选符合条件元素,reduce累积应用函数处理元素。变量作用域遵循LEGB规则,依次搜索局部(Local)、嵌套(Enclosing)、全局(Global)和内建(Built-in)作用域,全局变量需用global声明才能在函数内修改,嵌套变量用nonlocal声明可修改。函数调用时开辟栈帧存储局部变量,调用结束后释放。递归函数通过自身调用解决子问题,需包含终止条件避免无限递归,常见应用如阶乘计算、斐波那契数列等。