第十章:AI进阶之---函数的定义与调用(一)
Python函数基础摘要 本文介绍了Python中函数的基本概念和使用方法。函数是预先编写好的可复用代码块,通过def关键字定义,包含函数名、参数列表和函数体。主要作用包括代码复用、逻辑封装和提高可读性。函数分为有返回值(使用return语句)和无返回值类型,调用时需注意参数传递机制:Python采用对象引用传递,对于可变对象(如列表)的修改会影响外部变量,而不可变对象(如数字)则不会。课程还涵盖
课时 11:函数的定义与调用
文章目录
一、导入
欢迎来到 Python 函数的世界!在前面的课程中,我们已经学习了基本的数据类型、条件语句和循环结构,这些内容让我们能够编写简单的程序。但随着程序变得越来越复杂,我们需要一种更高效的方式来组织和复用代码。这就是我们今天要学习的主题 —— 函数。
函数是 Python 编程的核心概念,它允许我们将代码块封装起来,赋予一个名称,然后在需要的时候调用它。函数就像是一个精心设计的工具,我们可以根据需要随时使用它,而不必重复编写相同的代码。
本节课的学习目标:
-
理解函数的概念和作用(代码复用、逻辑封装)
-
掌握函数定义的语法和规范
-
学会正确调用函数并传递参数
-
理解有返回值和无返回值函数的区别
-
掌握参数传递方式(值传递与引用传递)
-
理解函数作用域问题
-
避免函数使用中的常见错误
函数是编程中最基本的代码组织单元,是实现代码复用和模块化的关键工具。在 Python 中,函数是一等公民,这意味着函数可以像其他数据类型一样被传递、赋值和操作。理解函数的概念和使用方法,是成为一名优秀程序员的必经之路。
二、函数的概念与作用
2.1 函数的定义与作用
函数是一段预先编写好的、具有特定功能的代码块,它可以被重复调用,从而实现代码的复用和逻辑的封装。简单来说,函数就像是一个 “黑盒子”,我们给它一些输入(参数),它会根据这些输入进行处理,并返回一个结果(返回值)。
函数的主要作用:
-
代码复用:避免重复编写相同或相似的代码,提高开发效率。
-
逻辑封装:将复杂的任务分解为多个简单的函数,使代码结构更清晰。
-
提高可读性:给代码块一个有意义的名称,使程序更易于理解。
-
便于维护:修改函数内部逻辑时,只需要在一个地方进行修改。
-
便于调试:可以独立测试每个函数的功能,更容易发现和解决问题。
示例:假设我们需要在程序中多次计算圆的面积,每次都编写3.14 * radius * radius
这样的代码会显得冗余且难以维护。如果我们将这段代码封装成一个名为circle_area
的函数,每次需要计算圆的面积时只需要调用这个函数即可,这样不仅减少了代码量,也提高了程序的可读性和可维护性。
2.2 函数与过程的区别
在计算机科学中,函数和过程是两个相关但不同的概念:
特性 | 函数 | 过程 |
---|---|---|
返回值 | 必须有一个返回值 | 可以没有返回值 |
主要用途 | 计算并返回结果 | 执行一系列操作 |
调用方式 | 通常作为表达式的一部分 | 通常作为独立的语句 |
在 Python 中,虽然技术上所有函数都有返回值(默认返回 None),但习惯上我们将有返回值的函数称为函数,将没有返回值(或返回 None)的函数称为过程。
示例:
# 函数(有返回值)
def add(a, b):
return a + b
# 过程(无返回值,或者说返回None)
def print_hello():
print("Hello, World!")
2.3 Python 中的函数类型
Python 提供了多种类型的函数,以满足不同的编程需求:
-
内置函数:Python 自带的函数,如
print()
、len()
、range()
等,可以直接使用。 -
用户自定义函数:由程序员根据需要定义的函数。
-
匿名函数(lambda 函数):没有函数名的简短函数,通常用于简单的表达式。
-
递归函数:在函数内部调用自身的函数,用于解决可以分解为相似子问题的问题。
-
生成器函数:使用
yield
关键字的函数,返回一个生成器对象,用于生成一系列值。
在本节课中,我们将主要关注用户自定义函数的定义和调用,其他类型的函数将在后续课程中学习。
三、函数的定义与调用
3.1 函数定义的语法
在 Python 中,使用def
关键字来定义函数,其基本语法结构如下:
def 函数名(参数列表):
"""文档字符串(可选)"""
函数体
return 返回值(可选)
各部分说明:
-
def
关键字:表示函数定义的开始,后跟函数名和参数列表。 -
函数名:遵循标识符命名规则,用于在程序中引用该函数。
-
参数列表:可以包含 0 个或多个参数,参数之间用逗号分隔。
-
冒号
:
:参数列表后必须跟一个冒号,表示函数体的开始。 -
文档字符串:用三引号
"""
包裹的字符串,用于描述函数的功能、参数和返回值,是可选的但强烈推荐。 -
函数体:缩进的代码块,包含函数执行的具体操作。
-
return
语句:可选,用于返回函数的执行结果。如果没有return
语句,函数将返回None
。
示例:定义一个计算两个数之和的函数:
def add(a, b):
"""计算两个数的和并返回结果"""
result = a + b
return result
说明:
-
函数名为
add
,接受两个参数a
和b
。 -
文档字符串描述了函数的功能。
-
函数体计算
a
和b
的和,并将结果赋给变量result
。 -
return
语句将结果返回给调用者。
3.2 函数调用的语法
定义函数后,可以通过函数名加括号的方式来调用它,其基本语法结构如下:
返回值 = 函数名(参数列表)
各部分说明:
-
函数名:要调用的函数的名称。
-
参数列表:与函数定义中的参数列表相对应的实际参数,可以是 0 个或多个,用逗号分隔。
-
返回值:可选,用于接收函数返回的结果。如果函数没有返回值(即返回
None
),可以省略。
示例:调用前面定义的add
函数:
# 调用add函数并将结果赋给变量sum_result
sum_result = add(3, 5)
print(sum_result) # 输出: 8
# 直接使用函数调用的结果
print(add(2, 4)) # 输出: 6
函数调用的执行流程:
-
当程序执行到函数调用语句时,会暂停当前代码的执行。
-
系统为被调用函数分配内存空间,用于存储函数的参数和局部变量。
-
将实际参数的值传递给形式参数。
-
执行函数体中的代码。
-
遇到
return
语句时,将返回值传递给调用者,并释放为被调用函数分配的内存空间。 -
程序继续执行函数调用语句之后的代码。
3.3 无返回值函数与有返回值函数的调用区别
在 Python 中,所有函数都有返回值,但根据是否有return
语句,函数可以分为无返回值函数和有返回值函数:
无返回值函数(返回 None):
def greet(name):
"""向指定的人打招呼"""
print(f"Hello, {name}!")
# 调用无返回值函数
greet("Alice") # 输出: Hello, Alice!
result = greet("Bob") # 输出: Hello, Bob!
print(result) # 输出: None
有返回值函数:
def multiply(a, b):
"""计算两个数的乘积并返回结果"""
return a * b
# 调用有返回值函数
product = multiply(3, 4)
print(product) # 输出: 12
# 直接使用返回值
print(multiply(2, 5) * 3) # 输出: 30
调用区别总结:
特性 | 无返回值函数 | 有返回值函数 |
---|---|---|
是否有 return 语句 | 没有或只有不带表达式的 return | 必须有带表达式的 return |
返回值 | None | 指定类型的值 |
调用方式 | 通常作为独立的语句 | 通常作为表达式的一部分 |
调用结果处理 | 不需要变量接收 | 通常需要变量接收结果 |
最佳实践:
-
当函数主要用于执行操作(如打印输出、修改文件等)时,应设计为无返回值函数。
-
当函数主要用于计算并返回结果时,应设计为有返回值函数。
-
即使函数没有返回值,也可以使用变量接收其返回值(此时变量的值为 None)。
3.4 参数传递机制
在 Python 中,参数传递采用的是对象引用传递(也称为共享传参)。这意味着函数接收的是实际参数的引用(内存地址),而不是参数值的副本。这种传递方式的行为取决于参数对象的可变性:
-
对于不可变对象(如数字、字符串、元组):函数接收到的是对象的引用,但由于对象本身不可变,函数内部对参数的修改不会影响外部变量。
-
对于可变对象(如列表、字典、集合):函数接收到的是对象的引用,函数内部对参数的修改会直接影响外部变量。
示例:
# 不可变对象作为参数
def modify_number(n):
n = 100 # 修改参数n的值
x = 10
modify_number(x)
print(x) # 输出: 10,x的值未改变
# 可变对象作为参数
def modify_list(lst):
lst.append(4) # 修改参数lst所指向的列表
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # 输出: [1, 2, 3, 4],my_list被修改了
参数传递机制的本质:
-
无论参数是可变还是不可变,传递的都是对象的引用。
-
对于不可变对象,虽然引用被传递,但由于对象本身不可变,无法通过引用修改原始对象。
-
对于可变对象,可以通过引用直接修改原始对象。
常见误解:
-
有人认为 Python 对不可变对象采用值传递,对可变对象采用引用传递。实际上,Python 始终传递对象引用,但行为因对象的可变性而异。
-
重要的不是参数的类型,而是对象是否可变。例如,字符串是不可变的,而列表是可变的。
四、函数参数详解
4.1 形式参数与实际参数
在函数定义和调用中,涉及到两种参数:
-
形式参数(形参):在函数定义中声明的参数,用于指定函数期望接收的数据类型和数量。
-
实际参数(实参):在函数调用时传递给函数的具体值,用于给形参赋值。
示例:
# 形参:a和b是形式参数
def subtract(a, b):
return a - b
# 实参:5和3是实际参数
result = subtract(5, 3)
print(result) # 输出: 2
形参与实参的关系:
-
形参是实参的占位符,在函数调用时,实参的值被传递给形参。
-
实参与形参在数量、类型和顺序上应该保持一致,否则会导致错误。
-
形参在函数内部作为局部变量使用,其作用域仅限于函数内部。
4.2 实参传递方式
在 Python 中,实参可以通过以下几种方式传递给函数:
-
位置传递:实参按照形参定义的顺序依次传递。
-
关键字传递:实参通过形参的名称传递,不考虑顺序。
-
混合传递:位置传递和关键字传递混合使用,但位置参数必须在关键字参数之前。
示例:
# 位置传递
def describe_person(name, age, city):
print(f"{name} is {age} years old and lives in {city}")
describe_person("Alice", 30, "New York") # 位置传递
# 关键字传递
describe_person(city="London", age=25, name="Bob") # 关键字传递,顺序无关
# 混合传递
describe_person("Charlie", age=35, city="Paris") # 位置参数在前,关键字参数在后
位置传递与关键字传递的对比:
特性 | 位置传递 | 关键字传递 |
---|---|---|
参数顺序 | 必须与形参顺序一致 | 可以任意顺序 |
可读性 | 较低,尤其当参数较多时 | 较高,参数含义明确 |
适用场景 | 参数较少且顺序明显时 | 参数较多或顺序不明显时 |
错误风险 | 较高,容易因顺序错误导致逻辑错误 | 较低,参数名称明确 |
混合传递的规则:
-
位置参数必须出现在关键字参数之前。
-
一个参数不能同时通过位置和关键字传递。
-
关键字参数必须是函数定义中存在的形参名称。
错误示例:
# 错误:位置参数在关键字参数之后
describe_person(age=30, "David", city="Sydney") # 会抛出SyntaxError
# 错误:重复传递参数
describe_person("Eve", age=28, name="Eve") # 会抛出TypeError
4.3 形参定义的不同形式
Python 允许在函数定义中使用多种形式的参数,以满足不同的编程需求:
-
位置参数:最基本的参数形式,必须按顺序传递。
-
默认参数:在函数定义时为参数指定默认值,调用时可以省略该参数。
-
可变参数(args):用于接收任意数量的位置参数,将这些参数收集到一个元组中。
-
关键字可变参数(kwargs):用于接收任意数量的关键字参数,将这些参数收集到一个字典中。
-
仅限关键字参数:必须通过关键字传递的参数,不能通过位置传递。
-
仅限位置参数:必须通过位置传递的参数,不能通过关键字传递。
示例:
# 位置参数和默认参数
def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("Alice") # 使用默认的message参数
greet("Bob", "Hi") # 覆盖默认的message参数
# 可变参数(*args)
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2, 3)) # 输出: 6
print(sum_all(4, 5, 6, 7)) # 输出: 22
# 关键字可变参数(**kwargs)
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Charlie", age=30, city="Paris")
参数组合的顺序规则:
当在一个函数定义中同时使用多种参数时,必须按照以下顺序:
位置参数 → 可变参数(*args) → 仅限关键字参数 → 关键字可变参数(**kwargs)
示例:
def complex_function(a, b, *args, c, d, **kwargs):
pass
4.4 参数解包(解构)
在 Python 中,可以使用 * 和 ** 运算符对序列和字典进行解包,以便在函数调用时传递参数:
-
序列解包(使用*):将序列中的元素作为位置参数传递给函数。
-
字典解包(使用**):将字典中的键值对作为关键字参数传递给函数。
示例:
# 序列解包
def divide(dividend, divisor):
return dividend // divisor, dividend % divisor
numbers = (10, 3)
quotient, remainder = divide(*numbers) # 使用*解包元组
print(f"Quotient: {quotient}, Remainder: {remainder}") # 输出: Quotient: 3, Remainder: 1
# 字典解包
def print_person_info(name, age, city):
print(f"{name} is {age} years old and lives in {city}")
person = {"name": "Alice", "age": 30, "city": "New York"}
print_person_info(**person) # 使用**解包字典
参数解包的应用场景:
-
当参数存储在序列或字典中,需要传递给函数时。
-
当需要将一个函数的输出作为另一个函数的输入时。
-
当需要动态构建参数列表时。
更多推荐
所有评论(0)