每天学习一点Python——函数调用的正确姿势
大家好!今天我们来聊聊Python中的函数调用。并顺便扩展一下异常处理的操作。
一、函数名和函数调用的区别
我们先来看第一段代码:
# 函数在Python中是值,可以赋值给变量
print("原始len函数:", len)
print("len的类型:", type(len))
代码解释:
- •
print("原始len函数:", len):这一行中有两个参数。第一个是字符串"原始len函数:",它会原样显示;第二个是len,注意这里没有括号,表示打印的是len这个函数本身,而不是调用它。 - •
print("len的类型:", type(len)):type()是一个内置函数,用来查看对象的类型。这里我们查看len这个函数的类型。
执行结果:
原始len函数: <built-in function len>
len的类型: <class 'builtin_function_or_method'>
看到结果了吗?当我们只写函数名而不加括号时,Python告诉我们这是一个"built-in function"(内置函数)。这就像告诉你"这是一个工具",而不是"使用这个工具"。
对比学习:函数名 vs 函数调用
二、重新赋值函数名会发生什么?
接下来这段代码很有意思:
# 可以将函数名重新赋值为其他值
len = "I'm not the len you're looking for."
print("\n重新赋值后的len:", len)
print("现在len的类型:", type(len))
代码解释:
- •
len = "I'm not ...":这行代码把变量名len重新赋值为一个字符串。是的,Python允许这样做! - • 第一个
print中的\n表示换行,让输出更清晰。
执行结果:
重新赋值后的len: I'm not the len you're looking for.
现在len的类型: <class 'str'>
看到变化了吗?len从内置函数变成了一个字符串!这是因为在Python中,函数名其实就是变量名,它保存着对函数的引用。我们可以改变这个引用,让它指向其他东西。
小Tip:别随便重命名内置函数
虽然Python允许重命名内置函数,但在实际编程中千万不要这样做!否则当你真的需要使用len()函数时,会发现它"消失"了。
三、如何恢复被覆盖的内置函数?
如果不小心覆盖了内置函数怎么办?看这里:
# 使用del删除绑定
del len
print("\n使用del后,len恢复为内置函数:", len)
代码解释:
执行结果:
使用del后,len恢复为内置函数: <built-in function len>
看到了吗?内置函数又回来了!del语句就像"橡皮擦",可以把我们之前错误的赋值擦掉。
四、函数必须被调用才会执行
这是很多初学者最容易犯错误的地方:
# 函数必须被调用才会执行
print("只写函数名不会执行:", len)
# 正确调用函数需要括号和参数
try:
len() # 这会报错,因为len需要参数
except TypeError as e:
print(f"错误: {e}")
# 正确调用方式
result = len("four")
print(f'len("four")的结果: {result}')
代码解释:
- 1.
print("只写函数名不会执行:", len):再次强调,只写len不会调用函数 - 2.
try: 和 except::这是异常处理结构。如果len()出错,程序不会崩溃,而是执行except中的代码 - 3.
len():这是错误的调用方式,因为len函数需要一个参数 - 4.
len("four"):这是正确的调用方式,给len函数传递字符串"four"
执行结果:
只写函数名不会执行: <built-in function len>
错误: len() takes exactly one argument (0 given)
len("four")的结果: 4
小Tip:记住函数的使用方法
每个函数都有自己的使用方法:
异常处理结构详解:
1. try: 代码块
2. except TypeError as e: 代码块
- •
as e:将异常对象赋值给变量e,方便获取错误信息
为什么需要异常处理?
场景对比:
没有异常处理(程序崩溃):
def divide_numbers(a, b):
return a / b
result = divide_numbers(10, 0) # ZeroDivisionError
print("程序继续执行") # 永远不会执行到这里
有异常处理(程序继续运行):
def divide_numbers(a, b):
try:
return a / b
except ZeroDivisionError as e:
print(f"错误:{e}")
return None
result = divide_numbers(10, 0) # 输出:错误:division by zero
print("程序继续执行") # 正常执行
实际应用场景
1. 文件操作
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("文件不存在,使用默认值")
content = "默认内容"
except PermissionError:
print("没有文件访问权限")
有无异常处理的区别
场景1:文件确实不存在
执行路径:
1. try: 尝试打开"data.txt" → 失败!抛出FileNotFoundError
2. 跳到except FileNotFoundError: → 执行这里的代码
3. 打印"文件不存在,使用默认值"
4. content = "默认内容"
5. 程序继续正常执行后面的代码
场景2:文件存在但没权限
执行路径:
1. try: 尝试打开"data.txt" → 失败!抛出PermissionError
2. 跳到except PermissionError: → 执行这里的代码
3. 打印"没有文件访问权限"
4. 程序继续执行(虽然content没有赋值,但至少没崩溃)
场景3:文件存在且有权限
执行路径:
1. try: 尝试打开"data.txt" → 成功!
2. content = file.read() → 读取内容
3. 跳过所有except块
4. 程序继续执行,content有实际的文件内容
关键意义:程序不会崩溃!
对比两种情况的程序行为:
没有try-except的情况:
# 硬编码方式
content = open("data.txt", "r").read()
# 如果文件不存在,则会直接报下面的错误:
Traceback (most recent call last):
File "test.py", line 1, in <module>
content = open("data.txt", "r").read()
FileNotFoundError: [Errno 2] No such file or directory: 'data.txt'
# 程序到此完全停止!后面的所有代码都不执行了
print("程序继续执行") # 永远不会执行到这里
有try-except的情况:
try:
content = open("data.txt", "r").read()
except FileNotFoundError:
print("文件不存在,使用默认值")
content = "默认内容"
print("程序继续执行") # 一定会执行到这里,无论文件是否存在
五、print函数的神奇之处
接下来这个例子会让你对函数有新的理解:
# print()函数会打印你让它打印的内容,但是它的返回值是None
return_value = print("What do I return?")
print(f"print()的返回值: {return_value}")
print(f"返回值的类型: {type(return_value)}")
# 直接打印None
print("None的值:", None)
代码解释:
- •
return_value = print("What do I return?"):调用print函数,并将其返回值赋给变量return_value - •
print(f"print()的返回值: {return_value}"):查看print函数的返回值 - •
type(return_value):查看返回值的类型
执行结果:
What do I return?
print()的返回值: None
返回值的类型: <class 'NoneType'>
None的值: None
对比学习:两种不同的函数
重要概念解释:
- •
None:Python中的特殊值,表示"什么都没有"或"空值" - •
print()函数的主要任务是在屏幕上打印你想打印的内容,而无需返回计算结果 - • 所有函数都有返回值,如果没有明确用
return语句返回,就自动返回None
小Tip:print()不能链式调用
# 错误:print()返回None,不能继续调用.upper()
result = print("Hello").upper() # 报错!
# 正确:字符串方法可以链式调用
text = "hello"
result = text.upper().replace("H", "J") # 正常
六、自定义函数的完整结构
现在我们来看如何创建自己的函数:
# 定义乘法函数
def multiply(x, y):
"""返回两个数的乘积"""
product = x * y
return product
# 调用函数
result = multiply(2, 4)
print(f"multiply(2, 4) = {result}")
# 查看函数文档
help(multiply)
代码解释:
- 1.
def multiply(x, y)::定义函数。def是"定义"的意思,multiply是函数名,x和y是参数 - 2.
"""返回两个数的乘积""":这是文档字符串(Docstring),说明函数的功能 - 3.
product = x * y:函数体,执行计算 - 5.
help(multiply):查看函数的帮助信息
执行结果:
multiply(2, 4) = 8
Help on function multiply in module __main__:
multiply(x, y)
返回两个数的乘积
什么是文档字符串?
文档字符串是放在函数、类或模块开头的一段文本,用于说明代码的功能、参数和返回值。它的作用:
1. 可执行代码 - Python会读取它
def add(a, b):
"""返回两个数的和"""
return a + b
# 可以通过 __doc__ 属性访问
print(add.__doc__) # 输出: 返回两个数的和
# 也可以通过help()函数查看
help(add) # 输出: 返回两个数的和
可以看到,用三个双引号把对函数的说明包起来并放到函数的开头,可以对你的函数进行解释说明。
文档字符串的格式规范
基本格式:
def 函数名(参数):
"""
一句话简要描述函数功能
详细描述(可选):
可以写多行,详细说明函数的作用、算法等
参数:
参数1: 描述
参数2: 描述
返回:
返回值的描述
异常:
可能抛出的异常(可选)
示例:
使用示例(可选)
"""
文档字符串 vs 多行字符串
它们看起来相似,但用途完全不同:
1. 文档字符串 (Docstring)
def greet(name):
"""
向指定的人问好
参数:
name: 要问候的人名
"""
print(f"Hello, {name}!")
- • 用途:说明代码功能,可以被Python读取(
help(greet))
2. 多行字符串
message = """这是一个
多行字符串
可以包含换行"""
print(message)
输出:
这是一个
多行字符串
可以包含换行
小Tip:一定要写文档字符串
文档字符串就像函数的"说明书"。想象一下,6个月后你再回来看自己写的代码,没有说明书还能记得每个函数是做什么的吗?
七、没有return语句的函数
有些函数没有return语句,它们会返回什么呢?
# 函数的不同部分
def greet(name):
"""
向指定的人问好
参数:
name: 要问候的人名
返回:
None (此函数有副作用)
"""
print(f"Hello, {name}!")
# 调用函数
greet("Alice")
# 即使没有return语句,函数也返回None
result = greet("Bob")
print(f"greet函数的返回值: {result}")
代码解释:
- •
def greet(name)::定义一个问候函数 - •
print(f"Hello, {name}!"):使用f-string格式化字符串 - •
result = greet("Bob"):调用函数并保存返回值
执行结果:
Hello, Alice!
Hello, Bob!
greet函数的返回值: None
重要规则:所有函数都有返回值
规则:如果函数没有明确的return语句,Python会自动返回None。
八、一个常见的错误
最后我们来看一个初学者常犯的错误:
# 这段代码会报错,因为multiply在定义前就被调用了
# 用三引号包裹,使其不执行
"""
# 错误示例
num = multiply(2, 4) # NameError: name 'multiply' is not defined
print(num)
def multiply(x, y):
return x * y
"""
# 正确写法
def multiply(x, y):
return x * y
num = multiply(2, 4)
print(f"正确顺序调用: multiply(2, 4) = {num}")
代码解释:
- 1. 错误示例中,在定义
multiply函数之前就调用了它
执行结果:
正确顺序调用: multiply(2, 4) = 8
小Tip:函数的"定义"和"调用"顺序
记住这个简单的规则:先定义,后调用。就像你要使用一个工具,必须先制造或购买这个工具一样。
总结
今天我们学习了Python函数调用的重要知识点:
- 1. 函数名 vs 函数调用:不加括号是"函数本身",加括号是"使用函数"
- 3. 异常处理:使用
try-except让程序更健壮 - 4. 函数的返回值:有的函数返回计算结果,有的函数返回
None - 6. 函数的定义:使用
def关键字,可以写文档字符串来对自己定义的函数进行解释说明
记住这些要点,你就能更好地理解和使用Python函数了!
📦 资源获取提示
关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码及配套练习数据集,一起动手掌握高效数据操作的核心技巧!
❤️ 支持我们
如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!
📚 学习资源说明
本文内容是基于《Python Basics: A Practical Introduction to Python 3》(Real Python)一书的学习笔记整理。
这本书是一本非常优秀的Python入门教材,推荐给所有想要系统学习Python的朋友们。
这本书的特点:
跟着这本书学习,配合我的笔记整理,相信你能更快掌握Python编程!
让我们一起坚持学习,每天进步一点点!💪