坚持用 清晰易懂的图解 + 代码语言,让每个知识点变得简单!
🚀呆头个人主页详情
🌱 呆头个人Gitee代码仓库
📌 呆头详细专栏系列
座右铭: “不患无位,患所以立。”
Python函数:从入门到精通,一文掌握函数编程精髓
- 前言
- 目录
- 一、函数是什么
- 1.函数的基本语法
- 二、函数参数
- 1.位置参数
- 2.默认参数
- 3.关键字参数
- 4.可变参数
- 5.关键字可变参数
- 6.参数类型对比
- 三、函数返回值
- 1.单值返回
- 2.多值返回
- 3.无返回值函数
- 四、变量作用域
- 1.局部变量与全局变量
- 2.使用global关键字
- 3.变量作用域可视化
- 五、函数的高级特性
- 1.函数作为对象
- 2.嵌套函数
- 3.闭包
- 4.装饰器
- 5.装饰器工作原理
- 六、函数的应用场景
- 1.数据处理
- 2.代码复用
- 3.回调函数
- 4.函数执行过程
- 七、函数参数传递机制
- 1.可变对象与不可变对象
- 2.参数传递机制可视化
- 八、函数设计最佳实践
- 1.单一职责原则
- 2.函数命名
- 3.参数设计
- 4.文档字符串
- 九、函数调试技巧
- 1.打印调试
- 2.使用断言
- 3.使用日志
- 十、函数性能优化
- 1.避免重复计算
- 2.使用生成器
- 3.使用适当的数据结构
- 十一、函数式编程
- 1.lambda函数
- 2.map、filter和reduce
- 3.列表推导式
- 十二、函数的常见错误与陷阱
- 1.默认参数陷阱
- 2.变量作用域问题
- 3.递归深度限制
- 总结
- 参考链接
- 关键词标签
前言
🔥 “灵根检测发现:阁下竟是万中无一的编程奇才!”
这是一个用修仙世界观解构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 ** exponentresult = power(2, 3) # 2的3次方 = 8
print(result) # 输出:8
2.默认参数
为参数提供默认值,调用时可以省略这些参数。
def power(base, exponent=2):"""默认计算平方"""return base ** exponentprint(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 = 0for num in numbers:total += numreturn totalprint(sum_all(1, 2, 3, 4, 5)) # 输出:15
5.关键字可变参数
使用**kwargs
接收任意数量的关键字参数,以字典形式存储。
def build_profile(**user_info):"""创建用户资料字典"""return user_infoprofile = 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 ** 2result = square(4)
print(result) # 输出:16
2.多值返回
Python函数可以同时返回多个值,实际上是返回一个元组。
def get_dimensions(length, width):"""计算矩形的周长和面积"""perimeter = 2 * (length + width)area = length * widthreturn perimeter, areap, 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 = 0def increment():global countercounter += 1return counterprint(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 + yreturn inner_functionadd_five = outer_function(5)
print(add_five(3)) # 输出:8
print(add_five(7)) # 输出:12
3.闭包
闭包是一个函数,它记住了创建它时的环境。
def make_multiplier(factor):"""创建一个乘法器"""def multiplier(number):return number * factorreturn multiplierdouble = make_multiplier(2)
triple = make_multiplier(3)print(double(5)) # 输出:10
print(triple(5)) # 输出:15
4.装饰器
装饰器是一种特殊的函数,它接受一个函数作为参数并返回一个新函数。
def timer_decorator(func):"""计时装饰器"""import timedef 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 resultreturn wrapper@timer_decorator
def slow_function():"""一个耗时的函数"""import timetime.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 + 1results.append(processed)return resultsdata = [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 mathreturn math.pi * dimensions[0] ** 2elif 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 ** 2def cube(x):return x ** 3numbers = [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 * bprint(f"中间结果: {intermediate}")result = intermediate / cprint(f"最终结果: {result}")return resulttry:complex_calculation(5, 2, 0)
except Exception as e:print(f"发生错误: {e}")
2.使用断言
断言可以帮助验证函数的前置条件和后置条件。
def divide(a, b):"""安全除法,确保除数不为零"""assert b != 0, "除数不能为零"return a / btry: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 * 2logging.info(f"项目 {item} 处理成功,结果: {result}")return resultexcept 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 = nelse:value = fibonacci(n-1) + fibonacci(n-2)# 缓存结果fibonacci_cache[n] = valuereturn value# 使用functools.lru_cache装饰器
from functools import lru_cache@lru_cache(maxsize=128)
def fibonacci_optimized(n):"""使用lru_cache优化的斐波那契函数"""if n <= 1:return nreturn fibonacci_optimized(n-1) + fibonacci_optimized(n-2)# 比较性能
import timedef measure_time(func, *args):start = time.time()result = func(*args)end = time.time()print(f"{func.__name__} 执行时间: {end - start:.6f} 秒")return resultn = 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 = 0for search_item in search_items:if search_item in items: # 列表查找是O(n)found += 1return found# 使用集合查找
def find_in_set(items_set, search_items):found = 0for search_item in search_items:if search_item in items_set: # 集合查找是O(1)found += 1return found# 比较性能
start = time.time()
found_list = find_in_list(data, search_items)
list_time = time.time() - startstart = time.time()
found_set = find_in_set(set(data), search_items)
set_time = time.time() - startprint(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 + yprint(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 itemsprint(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 itemsprint(add_item_fixed("apple")) # 输出:['apple']
print(add_item_fixed("banana")) # 输出:['banana']
2.变量作用域问题
在函数内部引用外部变量时,需要注意作用域规则。
x = 10def update_x():x = 20 # 创建了一个局部变量x,而不是修改全局变量print(f"函数内部x = {x}")update_x()
print(f"函数外部x = {x}")
# 输出:
# 函数内部x = 20
# 函数外部x = 10def update_x_correctly():global xx = 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 1return 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 accreturn factorial_tail(n - 1, n * acc)# 或者使用循环代替递归
def factorial_loop(n):result = 1for i in range(1, n + 1):result *= ireturn result
总结
随着Python的不断发展,函数的特性也在不断丰富。类型提示、异步编程、函数式编程特性的引入,都为我们提供了更多的工具来编写高质量的代码。保持学习的热情,跟上这些新特性的步伐,才能在Python编程的道路上走得更远。
希望这篇文章能够帮助你更好地理解和使用Python函数,让我们一起用函数的力量,构建更加优雅、高效的Python程序!
📢 如果你也喜欢这种"不呆头"的技术风格:
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给"不写八股文"的技术分享一点鼓励
🔖 【收藏】 把这些"奇怪但有用"的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最"呆头"的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻
参考链接
- Python官方文档 - 函数定义
- PEP 484 – Type Hints
- Python函数式编程指南
- Python装饰器的工作原理
- Python异步编程入门
关键词标签
#Python函数 #函数编程 #装饰器 #闭包 #异步函数 #函数式编程 #类型提示