# 未使用函数的复杂逻辑
data = [1, 2, 3, 4, 5]
sum_val = 0for num in data:
sum_val += num
average = sum_val / len(data)
print(f"平均值:{average}")
# 使用函数模块化defcalculate_average(data):
(data) / (data)
data = [, , , , ]
()
return
sum
len
1
2
3
4
5
print
f"平均值:{calculate_average(data)}"
代码逻辑清晰,每个函数聚焦一个子任务。
便于团队协作开发。
提升代码可读性
问题场景:长段代码缺乏注释时,阅读者需要逐行理解逻辑。
函数解决方案:通过函数名称直接表明代码意图。
# 难读的代码
x = 5
y = 10
result = (x ** 2 + y ** 2) ** 0.5# 使用函数自解释defcalculate_hypotenuse(a, b):
return (a ** 2 + b ** 2) ** 0.5
result = calculate_hypotenuse(5, 10)
defload_data(filename):
# 假设此处可能抛出文件不存在的错误withopen(filename, 'r') as f:
return f.read()
defprocess_data(data):
# 此处可能处理数据时出错return data.upper()
try:
data = load_data("data.txt")
result = process_data(data)
except Exception as e:
print(f"错误发生在:{e}")
快速定位问题函数。
通过单元测试单独验证每个函数。
支持代码复用与库开发
问题场景:通用功能(如数据验证、数学计算)需要在多个项目中重复实现。
函数解决方案:将通用函数封装为模块(.py 文件),供其他程序导入。
# utils.pydefis_prime(n):
if n <= 1:
returnFalsefor i inrange(2, int(n ** 0.5) + 1):
if n % i == 0:
returnFalsereturnTrue# main.pyfrom utils import is_prime
print(is_prime(17)) # 输出:True
积累个人代码库,提升开发效率。
促进开源社区协作(如 numpy、requests 等库)。
实现递归算法
问题场景:某些问题(如阶乘、斐波那契数列)天然适合递归解决。
函数解决方案:函数可以调用自身,简化递归逻辑。
deffactorial(n):
if n == 0:
return1else:
return n * factorial(n - 1)
print(factorial(5)) # 输出:120
函数在执行时使用函数局部变量符号表,所有函数变量赋值都存在局部符号表中;引用变量时,首先,在局部符号表里查找变量,然后,是外层函数局部符号表,再是全局符号表,最后是内置名称符号表。因此,尽管可以引用全局变量和外层函数的变量,但最好不要在函数内直接赋值(除非是 global 语句定义的全局变量,或 nonlocal 语句定义的外层函数变量)。
deffib(n):
"""Print a Fibonacci series less than n."""
a, b = 0, 1while a < n:
print(a, end=' ')
a, b = b, a + b
f = fib # 将函数对象赋值给 f
f(100) # 调用 f
fib(100) # 调用 fib()
deffib2(n):
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1while a < n:
result.append(a)
a, b = b, a + b
return result
f100 = fib2(100) # 调用它print(f100) # 输出结果 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
函数定义详解
函数定义支持可变数量的参数。这里列出三种可以组合使用的形式。
默认值参数
为参数指定默认值是非常有用的方式。调用函数时,可以使用比定义时更少的参数,例如:
defask_ok(prompt, retries=4, reminder='Please try again!'):
whileTrue:
reply = input(prompt)
if reply in {'y', 'ye', 'yes'}:
returnTrueif reply in {'n', 'no', 'nop', 'nope'}:
returnFalse
retries = retries - 1if retries < 0:
raise ValueError('invalid user response')
print(reminder)
该函数有三种调用方式:
只给出必选实参:ask_ok('Do you really want to quit?')
给出一个可选实参:ask_ok('OK to overwrite the file?', 2)
给出所有实参:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
try:
# 异常捕获
result = ask_ok("Do you want to continue? ")
if result:
print("You agreed!")
else:
print("You disagreed!")
except ValueError as e:
print(e)
默认值在定义作用域里的函数定义中求值所以:
i = 5deff(arg=i):
print(arg)
i = 6
f()
上例输出的是 5。为什么呢???
这里创建了一个全局变量 i,并将其赋值为 5。在定义函数 f 时,默认参数 arg 的值被设定为当前全局变量 i 的值,也就是 5。需要注意的是,Python 函数的默认参数值是在函数定义时就确定下来的,而不是在函数调用时确定。
将全局变量 i 的值修改为 6,但这并不会影响函数 f 中默认参数 arg 的值,因为 arg 的默认值在函数定义时就已经确定为 5 了。
defcheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
-- Do you have any Limburger ?-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
customer : John Cleese
sketch : Cheese Shop Sketch
pos_only_arg(arg=1)
# Traceback (most recent call last):# File "<stdin>", line 1, in <module># TypeError: pos_only_arg() got some positional-only arguments passed as keyword arguments:'arg'
第三个函数 kwd_only_arg 如在函数定义中通过 * 所指明的那样只允许关键字参数。
位置参数:报错
kwd_only_arg(3)
# Traceback (most recent call last):# File "<stdin>", line 1, in <module># TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given
关键字参数:
kwd_only_arg(arg=3) # 输出 3
最后一个函数在同一个函数定义中,使用了全部三种调用惯例:
全部位置参数:报错!
combined_example(1, 2, 3)
# Traceback (most recent call last):# File "<stdin>", line 1, in <module># TypeError: combined_example() takes 2 positional arguments but 3 were given