坚持用 清晰易懂的图解 + 代码语言,让每个知识点变得简单!
🚀呆头个人主页详情
🌱 呆头个人Gitee代码仓库
📌 呆头详细专栏系列
座右铭: “不患无位,患所以立。”
Python函数:从入门到精通,一文掌握函数编程精髓
前言
🔥 “灵根检测发现:阁下竟是万中无一的编程奇才!”
这是一个用修仙世界观解构Python学习的硬核专栏:
- 练气期:变量/循环/函数(基础心法)
- 筑基期:面向对象/异常处理(护体罡气)
- 金丹期:爬虫/数据分析(神通初成)
- 元婴期:Django/机器学习(开辟紫府)
✍️ 你将获得:
✅ 每章配套「渡劫雷法」(实战项目)
✅ 避免走火入魔的「心魔警示」(避坑指南)
✅ 飞升大能的「神识传承」(大佬代码赏析)“三千大道,Py为尊。本座在此布道,助你斩尽BUG,证道代码金仙!”
(正文开始👇)
目录
一、函数是什么
函数本质上是一段可重用的代码块,它接收输入(参数),执行特定任务,并返回结果。在Python中,函数不仅仅是组织代码的方式,更是实现抽象和封装的重要工具。
1.函数的基本语法
Python函数定义的基本语法如下:
def 函数名(参数1, 参数2, ...):
"""文档字符串:描述函数功能"""
# 函数体
return 返回值 # 可选
一个简单的函数示例:
def greet(name):
"""向指定的人打招呼"""
return f"你好,{name}!"
# 调用函数
message = greet("小明")
print(message) # 输出:你好,小明!
在这个例子中,greet
是函数名,name
是参数,函数体很简单,只有一行代码,返回一个格式化的字符串。
二、函数参数
Python函数的参数系统非常灵活,提供了多种参数类型来满足不同的需求。
1.位置参数
最基本的参数类型,调用时按照定义的顺序传递参数值。
def power(base, exponent):
"""计算base的exponent次方"""
return base ** exponent
result = power(2, 3) # 2的3次方 = 8
print(result) # 输出:8
2.默认参数
为参数提供默认值,调用时可以省略这些参数。
def power(base, exponent=2):
"""默认计算平方"""
return base ** exponent
print(power(3)) # 3的2次方 = 9
print(power(3, 4)) # 3的4次方 = 81
3.关键字参数
通过参数名指定参数值,可以不按顺序传参。
def describe_pet(animal_type, pet_name):
"""描述宠物信息"""
return f"我有一只{animal_type},它叫{pet_name}。"
# 使用关键字参数
print(describe_pet(pet_name="球球", animal_type="猫")) # 输出:我有一只猫,它叫球球。
4.可变参数
使用*args
接收任意数量的位置参数,以元组形式存储。
def sum_all(*numbers):
"""计算所有参数的和"""
total = 0
for num in numbers:
total += num
return total
print(sum_all(1, 2, 3, 4, 5)) # 输出:15
5.关键字可变参数
使用**kwargs
接收任意数量的关键字参数,以字典形式存储。
def build_profile(**user_info):
"""创建用户资料字典"""
return user_info
profile = build_profile(name="小明", age=25, city="北京", hobby="编程")
print(profile) # 输出:{'name': '小明', 'age': 25, 'city': '北京', 'hobby': '编程'}
6.参数类型对比
下面的表格总结了Python函数的不同参数类型:
参数类型 | 语法 | 示例 | 特点 |
---|---|---|---|
位置参数 | def func(param1, param2) |
func(1, 2) |
必须按顺序提供所有参数 |
默认参数 | def func(param1, param2=value) |
func(1) 或 func(1, 3) |
可选参数,有默认值 |
关键字参数 | def func(param1, param2) |
func(param2=2, param1=1) |
通过参数名指定,顺序灵活 |
可变位置参数 | def func(*args) |
func(1, 2, 3, 4) |
接收任意数量的位置参数 |
关键字可变参数 | def func(**kwargs) |
func(a=1, b=2, c=3) |
接收任意数量的关键字参数 |
三、函数返回值
Python函数可以返回单个值、多个值,或者不返回任何值(默认返回None
)。
1.单值返回
最常见的返回形式,函数执行完毕后返回一个值。
def square(number):
"""返回数字的平方"""
return number ** 2
result = square(4)
print(result) # 输出:16
2.多值返回
Python函数可以同时返回多个值,实际上是返回一个元组。
def get_dimensions(length, width):
"""计算矩形的周长和面积"""
perimeter = 2 * (length + width)
area = length * width
return perimeter, area
p, a = get_dimensions(5, 3)
print(f"周长:{p},面积:{a}") # 输出:周长:16,面积:15
3.无返回值函数
如果函数没有return
语句,或者return
后面没有表达式,函数将返回None
。
def greet(name):
"""打印问候语,无返回值"""
print(f"你好,{name}!")
result = greet("小红") # 打印:你好,小红!
print(result) # 输出:None
四、变量作用域
理解变量作用域对于编写正确的函数至关重要。Python中有局部作用域和全局作用域。
1.局部变量与全局变量
# 全局变量
message = "你好,世界!"
def greet():
# 局部变量
name = "小明"
print(f"{message} {name}")
greet() # 输出:你好,世界! 小明
# print(name) # 错误:name不在全局作用域中
2.使用global关键字
使用global
关键字可以在函数内部修改全局变量。
counter = 0
def increment():
global counter
counter += 1
return counter
print(increment()) # 输出:1
print(increment()) # 输出:2
print(counter) # 输出:2
3.变量作用域可视化
图1:变量作用域图 - 展示了Python中全局变量和局部变量的作用范围及相互影响
五、函数的高级特性
Python函数具有许多高级特性,使其成为一种强大的编程工具。
1.函数作为对象
在Python中,函数是一等公民,可以像其他对象一样被赋值、传递和返回。
def greet(name):
return f"你好,{name}!"
# 函数赋值给变量
say_hello = greet
print(say_hello("小明")) # 输出:你好,小明!
# 函数作为参数传递
def apply_function(func, value):
return func(value)
result = apply_function(greet, "小红")
print(result) # 输出:你好,小红!
2.嵌套函数
在函数内部定义另一个函数,内部函数可以访问外部函数的变量。
def outer_function(x):
"""外部函数"""
def inner_function(y):
"""内部函数"""
return x + y
return inner_function
add_five = outer_function(5)
print(add_five(3)) # 输出:8
print(add_five(7)) # 输出:12
3.闭包
闭包是一个函数,它记住了创建它时的环境。
def make_multiplier(factor):
"""创建一个乘法器"""
def multiplier(number):
return number * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 输出:10
print(triple(5)) # 输出:15
4.装饰器
装饰器是一种特殊的函数,它接受一个函数作为参数并返回一个新函数。
def timer_decorator(func):
"""计时装饰器"""
import time
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 执行时间:{end_time - start_time:.4f} 秒")
return result
return wrapper
@timer_decorator
def slow_function():
"""一个耗时的函数"""
import time
time.sleep(1)
return "函数执行完毕"
print(slow_function())
# 输出:
# 函数 slow_function 执行时间:1.0010 秒
# 函数执行完毕
5.装饰器工作原理
图2:装饰器工作原理时序图 - 展示了Python装饰器的执行流程和工作机制
六、函数的应用场景
Python函数在不同场景下有着广泛的应用。下面我们来看几个典型的应用场景。
1.数据处理
函数可以用于处理和转换数据。
def process_data(data_list):
"""处理数据列表,返回处理后的结果"""
results = []
for item in data_list:
# 数据处理逻辑
processed = item * 2 + 1
results.append(processed)
return results
data = [1, 2, 3, 4, 5]
processed_data = process_data(data)
print(processed_data) # 输出:[3, 5, 7, 9, 11]
2.代码复用
函数是实现代码复用的基本单位。
def calculate_area(shape, *dimensions):
"""计算不同形状的面积"""
if shape == "rectangle":
return dimensions[0] * dimensions[1]
elif shape == "circle":
import math
return math.pi * dimensions[0] ** 2
elif shape == "triangle":
return 0.5 * dimensions[0] * dimensions[1]
else:
return None
# 计算不同形状的面积
rectangle_area = calculate_area("rectangle", 5, 4)
circle_area = calculate_area("circle", 3)
triangle_area = calculate_area("triangle", 6, 8)
print(f"矩形面积:{rectangle_area}") # 输出:矩形面积:20
print(f"圆形面积:{circle_area:.2f}") # 输出:圆形面积:28.27
print(f"三角形面积:{triangle_area}") # 输出:三角形面积:24.0
3.回调函数
函数可以作为参数传递给其他函数,实现回调机制。
def apply_operation(numbers, operation):
"""对数字列表应用指定操作"""
return [operation(num) for num in numbers]
def square(x):
return x ** 2
def cube(x):
return x ** 3
numbers = [1, 2, 3, 4, 5]
squared = apply_operation(numbers, square)
cubed = apply_operation(numbers, cube)
print(f"平方结果:{squared}") # 输出:平方结果:[1, 4, 9, 16, 25]
print(f"立方结果:{cubed}") # 输出:立方结果:[1, 8, 27, 64, 125]
4.函数执行过程
让我们通过一个图表来可视化函数的执行过程:
图4:函数执行旅程图 - 展示了Python函数从调用到返回的完整执行过程
七、函数参数传递机制
Python的参数传递机制是"传对象引用",这意味着函数接收的是对象的引用,而不是对象的副本。
1.可变对象与不可变对象
理解可变对象和不可变对象对于理解函数参数传递至关重要。
# 不可变对象作为参数
def modify_string(s):
s = s + " World"
print(f"函数内部:{s}")
text = "Hello"
modify_string(text)
print(f"函数外部:{text}")
# 输出:
# 函数内部:Hello World
# 函数外部:Hello
# 可变对象作为参数
def modify_list(lst):
lst.append(4)
print(f"函数内部:{lst}")
numbers = [1, 2, 3]
modify_list(numbers)
print(f"函数外部:{numbers}")
# 输出:
# 函数内部:[1, 2, 3, 4]
# 函数外部:[1, 2, 3, 4]
2.参数传递机制可视化
图5:参数类型分布饼图 - 展示了Python中不同类型参数的使用比例
八、函数设计最佳实践
编写高质量的函数需要遵循一些最佳实践。
1.单一职责原则
每个函数应该只做一件事,并且做好。
# 不好的设计:函数做了太多事情
def process_and_save_data(data, filename):
# 处理数据
processed_data = []
for item in data:
processed_data.append(item * 2)
# 保存数据
with open(filename, 'w') as f:
for item in processed_data:
f.write(f"{item}\n")
return processed_data
# 好的设计:职责分离
def process_data(data):
"""只负责处理数据"""
return [item * 2 for item in data]
def save_data(data, filename):
"""只负责保存数据"""
with open(filename, 'w') as f:
for item in data:
f.write(f"{item}\n")
# 使用
data = [1, 2, 3, 4, 5]
processed = process_data(data)
save_data(processed, "output.txt")
2.函数命名
函数名应该清晰地表达函数的功能,通常使用动词或动词短语。
# 不好的命名
def x(a, b):
return a + b
# 好的命名
def add_numbers(a, b):
return a + b
3.参数设计
函数参数应该设计得直观且易于使用。
# 不好的参数设计
def create_user(p1, p2, p3, p4, p5):
# ...
pass
# 好的参数设计
def create_user(username, email, password, first_name=None, last_name=None):
# ...
pass
4.文档字符串
为函数添加文档字符串,说明函数的功能、参数和返回值。
def calculate_discount(price, discount_rate):
"""
计算折扣后的价格。
参数:
price (float): 原始价格
discount_rate (float): 折扣率,0到1之间的小数
返回:
float: 折扣后的价格
示例:
>>> calculate_discount(100, 0.2)
80.0
"""
if not 0 <= discount_rate <= 1:
raise ValueError("折扣率必须在0到1之间")
return price * (1 - discount_rate)
“函数应该做一件事,只做一件事,并且把这件事做好。” —— Robert C. Martin(Uncle Bob)
九、函数调试技巧
调试函数是编程过程中不可避免的一部分。以下是一些有用的调试技巧。
1.打印调试
最简单的调试方法是使用print
语句输出变量值和执行流程。
def complex_calculation(a, b, c):
print(f"输入参数: a={a}, b={b}, c={c}")
intermediate = a * b
print(f"中间结果: {intermediate}")
result = intermediate / c
print(f"最终结果: {result}")
return result
try:
complex_calculation(5, 2, 0)
except Exception as e:
print(f"发生错误: {e}")
2.使用断言
断言可以帮助验证函数的前置条件和后置条件。
def divide(a, b):
"""安全除法,确保除数不为零"""
assert b != 0, "除数不能为零"
return a / b
try:
result = divide(10, 0)
except AssertionError as e:
print(f"断言错误: {e}")
3.使用日志
对于复杂的应用程序,使用日志比打印更灵活。
import logging
# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
def process_item(item):
"""处理单个项目"""
logging.debug(f"开始处理项目: {item}")
try:
result = item * 2
logging.info(f"项目 {item} 处理成功,结果: {result}")
return result
except Exception as e:
logging.error(f"处理项目 {item} 时出错: {e}")
raise
# 使用函数
items = [1, 2, "3", 4]
for item in items:
try:
process_item(item)
except Exception as e:
logging.warning(f"跳过项目,继续处理下一个")
十、函数性能优化
优化函数性能可以从多个方面入手。
1.避免重复计算
使用缓存可以避免重复计算,提高性能。
# 使用字典作为缓存
fibonacci_cache = {}
def fibonacci(n):
"""计算斐波那契数列的第n项"""
# 检查缓存
if n in fibonacci_cache:
return fibonacci_cache[n]
# 计算值
if n <= 1:
value = n
else:
value = fibonacci(n-1) + fibonacci(n-2)
# 缓存结果
fibonacci_cache[n] = value
return value
# 使用functools.lru_cache装饰器
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci_optimized(n):
"""使用lru_cache优化的斐波那契函数"""
if n <= 1:
return n
return fibonacci_optimized(n-1) + fibonacci_optimized(n-2)
# 比较性能
import time
def measure_time(func, *args):
start = time.time()
result = func(*args)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.6f} 秒")
return result
n = 35
measure_time(fibonacci, n)
measure_time(fibonacci_optimized, n)
2.使用生成器
对于处理大量数据的函数,使用生成器可以减少内存使用。
# 使用列表(一次性加载所有数据到内存)
def get_squares_list(n):
"""返回0到n-1的平方列表"""
return [i**2 for i in range(n)]
# 使用生成器(按需生成数据)
def get_squares_generator(n):
"""返回0到n-1的平方生成器"""
for i in range(n):
yield i**2
# 使用列表
squares_list = get_squares_list(1000000) # 立即分配大量内存
# 使用生成器
squares_gen = get_squares_generator(1000000) # 不分配额外内存
for i, square in enumerate(squares_gen):
if i < 10: # 只处理前10个
print(square)
else:
break
3.使用适当的数据结构
选择合适的数据结构可以显著提高函数性能。
import time
import random
# 准备测试数据
data = list(range(10000))
random.shuffle(data)
search_items = random.sample(data, 1000)
# 使用列表查找
def find_in_list(items, search_items):
found = 0
for search_item in search_items:
if search_item in items: # 列表查找是O(n)
found += 1
return found
# 使用集合查找
def find_in_set(items_set, search_items):
found = 0
for search_item in search_items:
if search_item in items_set: # 集合查找是O(1)
found += 1
return found
# 比较性能
start = time.time()
found_list = find_in_list(data, search_items)
list_time = time.time() - start
start = time.time()
found_set = find_in_set(set(data), search_items)
set_time = time.time() - start
print(f"列表查找时间: {list_time:.6f} 秒")
print(f"集合查找时间: {set_time:.6f} 秒")
print(f"性能提升: {list_time/set_time:.2f}倍")
十一、函数式编程
Python支持函数式编程范式,提供了许多函数式编程的特性。
1.lambda函数
lambda函数是一种小型匿名函数,可以在需要函数对象的地方使用。
# 常规函数
def add(x, y):
return x + y
# 等价的lambda函数
add_lambda = lambda x, y: x + y
print(add(3, 5)) # 输出:8
print(add_lambda(3, 5)) # 输出:8
2.map、filter和reduce
这些高阶函数是函数式编程的核心。
# map:对列表中的每个元素应用函数
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # 输出:[1, 4, 9, 16, 25]
# filter:过滤列表中的元素
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # 输出:[2, 4]
# reduce:将列表中的元素累积为单个值
from functools import reduce
product = reduce(lambda x, y: x * y, numbers)
print(product) # 输出:120 (1*2*3*4*5)
3.列表推导式
列表推导式是一种简洁的创建列表的方式,可以替代map和filter的组合。
numbers = [1, 2, 3, 4, 5]
# 使用map和filter
squared_evens = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers)))
# 使用列表推导式
squared_evens_comp = [x**2 for x in numbers if x % 2 == 0]
print(squared_evens) # 输出:[4, 16]
print(squared_evens_comp) # 输出:[4, 16]
十二、函数的常见错误与陷阱
编写函数时,有一些常见的错误和陷阱需要避免。
1.默认参数陷阱
使用可变对象作为默认参数值可能导致意外行为。
# 错误的做法
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("apple")) # 输出:['apple']
print(add_item("banana")) # 输出:['apple', 'banana'] - 可能不是预期结果
# 正确的做法
def add_item_fixed(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item_fixed("apple")) # 输出:['apple']
print(add_item_fixed("banana")) # 输出:['banana']
2.变量作用域问题
在函数内部引用外部变量时,需要注意作用域规则。
x = 10
def update_x():
x = 20 # 创建了一个局部变量x,而不是修改全局变量
print(f"函数内部x = {x}")
update_x()
print(f"函数外部x = {x}")
# 输出:
# 函数内部x = 20
# 函数外部x = 10
def update_x_correctly():
global x
x = 20 # 修改全局变量
print(f"函数内部x = {x}")
update_x_correctly()
print(f"函数外部x = {x}")
# 输出:
# 函数内部x = 20
# 函数外部x = 20
3.递归深度限制
Python对递归深度有限制,超过限制会导致栈溢出。
import sys
print(f"最大递归深度: {sys.getrecursionlimit()}")
def factorial(n):
"""计算阶乘"""
if n <= 1:
return 1
return n * factorial(n - 1)
try:
result = factorial(1000) # 可能导致栈溢出
except RecursionError as e:
print(f"递归错误: {e}")
# 使用尾递归优化(Python不会自动优化尾递归)
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return factorial_tail(n - 1, n * acc)
# 或者使用循环代替递归
def factorial_loop(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
总结
随着Python的不断发展,函数的特性也在不断丰富。类型提示、异步编程、函数式编程特性的引入,都为我们提供了更多的工具来编写高质量的代码。保持学习的热情,跟上这些新特性的步伐,才能在Python编程的道路上走得更远。
希望这篇文章能够帮助你更好地理解和使用Python函数,让我们一起用函数的力量,构建更加优雅、高效的Python程序!
📢 如果你也喜欢这种"不呆头"的技术风格:
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给"不写八股文"的技术分享一点鼓励
🔖 【收藏】 把这些"奇怪但有用"的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最"呆头"的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻
参考链接
关键词标签
#Python函数 #函数编程 #装饰器 #闭包 #异步函数 #函数式编程 #类型提示