hello,大家好。
又见面啦,我是星禾。
在过去的5年时间里,我的身份从大厂程序员→全职宝妈→米核AI合伙人,个人的生活经历也可以称之为丰富吧。当然这几年也尝试过一些其他创业或者变现项目,目前all in米核AI,专注于分享一些AI前沿的知识和见解。大家如果有什么想法或者想入局AI赛道,可以找到我cuizh002。我会给大家提供一些基础的AI资料。当然如果你有什么想法,我们也可以一起交流。
好了,话不多说,我们进入今天的正题。
Python函数基础:从入门到精通
聊函数之前,先说个我自己的经历。
刚学Python那会儿,写代码全靠复制粘贴。同样的逻辑,这边要处理一遍,那边要处理一遍,复制过来粘贴过去。代码写长了之后,我愣了——我发现改一处逻辑,要找到所有复制的地方一个个改,稍不留神就漏掉一两个。
说实话,写代码的时候真的觉得复制粘贴很方便,总会觉得"就这么点代码,有必要封装吗?"。但回头一看,真的是后期维护太头大了!
我们一定要记得:函数这东西,就是帮你偷懒的。而且维护起来超方便!
那么今天我们就来聊聊Python函数,今天这一篇就让你实现从"会用"到"用好"的逆袭。
▶▶ 函数是什么?
函数就像是一个自动售货机。
你投入一些东西(参数),它内部帮你处理,最后给你返回结果(返回值)。
用代码表示就是这样:
def greet(name): """打招呼函数""" return f"你好,{name}!"
解释一下每个部分:
| |
|---|
def | Python定义函数的关键字,就像"我要开始造一个机器了" |
greet | |
(name) | |
return | |
调用的时候这样用:
result = greet("张三")print(result) # 输出:你好,张三!
▶▶ 参数传递:怎么给函数喂数据?
参数传递有几种方式,我一个个说。
1. 位置参数
就像填表,按顺序来。第一个参数对应第一个位置,第二个参数对应第二个位置,不能乱。
def introduce(name, age, city): print(f"我叫{name},今年{age}岁,来自{city}")introduce("张三", 25, "北京")# 输出:我叫张三,今年25岁,来自北京# 顺序错了就尴尬了introduce("北京", 25, "张三") # 输出:我叫北京,今年25岁,来自张三
怎么理解呢?就像你说"我叫张三,今年25岁",结果顺序错了,变成"我叫北京,今年25岁"——这人怕不是个地名成精了。
2. 关键字参数
不按顺序也行,但要报名字。Python会根据参数名匹配,不care顺序。
introduce(city="深圳", name="李四", age=30)# 输出:我叫李四,今年30岁,来自深圳
3. 默认参数
有些事不用说,大家都知道。比如打招呼,你说"你好"就行,不用每次都说"今天天气不错,我们开始打招呼吧"。
def greet(name, greeting="你好"): return f"{greeting},{name}!"print(greet("张三")) # 你好,张三!print(greet("张三", "早上好")) # 早上好,张三!
▶▶ 避坑指南:默认参数的坑
这里有个大坑,默认参数必须是不可变对象!否则会出问题:
# 错误示范def add_item(item, items=[]): items.append(item) return itemsprint(add_item("apple")) # ['apple']print(add_item("banana")) # ['banana', 'apple']
等等,第二个输出怎么有两个?不是说好只有一个"banana"吗?
因为Python函数的默认参数只在函数定义时计算一次,之后就一直用同一个对象了。你每次append,其实都是在往同一个列表里加东西。
正确做法是这样:
# 正确示范def add_item(item, items=None): if items is None: items = [] items.append(item) return itemsprint(add_item("apple")) # ['apple']print(add_item("banana")) # ['banana']
记住:默认参数用None,而不是可变对象。
▶▶ 可变参数:一次接收多个数据
有时候你不知道要传多少个参数怎么办?用args和*kwargs。
*args:接收任意数量的位置参数
def sum_all(*numbers): total = 0 for num in numbers: total += num return totalprint(sum_all(1, 2, 3)) # 6print(sum_all(10, 20, 30, 40)) # 100
*numbers会把所有位置参数打包成一个元组。调用的时候随便传几个数都行。
kwargs:接收任意数量的关键字参数
def print_info(**info): for key, value in info.items(): print(f"{key}: {value}")print_info(name="张三", age=25, city="北京")# 输出:# name: 张三# age: 25# city: 北京
info会把所有关键字参数打包成一个字典。
▶▶ 返回值:函数给我们的礼物
函数处理完数据,总得把结果还给你吧?这个"还"的动作,就是return。
没有return的函数
def say_hello(name): print(f"你好,{name}!")result = say_hello("张三")print(result) # None
如果你不用print而用return,函数会返回一个None。这就像你让朋友帮你买东西,他买完了但没给你,你手上还是空的。
有return的函数
def add(a, b): return a + bresult = add(1, 2)print(result) # 3
return还能返回多个值
def get_student_info(): name = "张三" age = 20 score = 95 return name, age, scoreinfo = get_student_info()print(info) # ('张三', 20, 95)# 也可以拆包name, age, score = get_student_info()print(name) # 张三
返回多个值的时候,Python其实返回的是一个元组。你可以整体接收,也可以拆开用。
▶▶ 变量作用域:变量的"势力范围"
变量不是哪都能用的,它有作用域的概念。
局部变量:只在函数里生效
def calculate(): result = 100 # 这是局部变量 return resultprint(calculate()) # 100print(result) # 报错!result不存在
result是在calculate函数内部定义的,出了这个函数,它就不存在了。就像你家的WiFi,只有在家里能连,出门就没了。
全局变量:到处都能用
total = 1000 # 这是全局变量def add_to_total(amount): global total # 声明我要用全局变量 total += amount return totalprint(add_to_total(500)) # 1500print(total) # 1500
但是,我建议你尽量少用global。因为全局变量会让代码变得难以理解——你不知道哪个函数会改它,什么时候改,改成什么。
更好的做法是,把需要用的值作为参数传进去,把结果通过return传出来。
▶▶ 匿名函数lambda:一行搞定简单函数
有时候你只是需要一个小函数,用一次就不用了。这时候可以用lambda,写起来更简洁。
# 普通函数def square(x): return x ** 2# lambda版本square = lambda x: x ** 2print(square(5)) # 25
lambda的常见用法
配合列表排序:
students = [ {"name": "张三", "score": 85}, {"name": "李四", "score": 92}, {"name": "王五", "score": 78}]# 按分数排序students.sort(key=lambda x: x["score"], reverse=True)print(students)# [{'name': '李四', 'score': 92}, {'name': '张三', 'score': 85}, {'name': '王五', 'score': 78}]
配合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]
▶▶ 递归函数:自己调用自己
递归就是函数调用自己。但关键是要有出口,否则会无限循环下去。
经典案例:计算阶乘
# 循环版本def factorial_loop(n): result = 1 for i in range(1, n + 1): result *= i return result# 递归版本def factorial_recursive(n): if n <= 1: return 1 # 终止条件 return n * factorial_recursive(n - 1) # 调用自己print(factorial_recursive(5)) # 120
递归三要素:
1. 明确的终止条件:什么时候停下来
2. 递归调用:自己调用自己
3. 问题规模缩小:每次调用都离终止条件更近
注意:递归太深会爆栈
递归虽然写起来简洁,但调用层次太深会出问题。建议超过100层的递归用循环代替。
▶▶ 高阶函数:函数也能当参数
高阶函数就是接收函数作为参数,或者返回函数的函数。
map、filter、reduce
from functools import reducenumbers = [1, 2, 3, 4, 5]# map:对每个元素做操作squared = list(map(lambda x: x ** 2, numbers))print(squared) # [1, 4, 9, 16, 25]# filter:筛选符合条件的元素even = list(filter(lambda x: x % 2 == 0, numbers))print(even) # [2, 4]# reduce:汇总所有元素total = reduce(lambda x, y: x + y, numbers)print(total) # 15
闭包:返回函数的函数
def make_multiplier(factor): def multiplier(number): return number * factor return multipliertimes_3 = make_multiplier(3)times_5 = make_multiplier(5)print(times_3(10)) # 30print(times_5(10)) # 50
make_multiplier返回了一个函数,这个返回的函数"记住"了factor的值。这就是闭包。
▶▶ 实用技巧总结
❌ 常见错误
✅ 推荐做法
1. 函数要短小:一个函数只做一件事
2. 起个好名字:函数名要见名知意,如 calculate_average()
3. 写文档字符串:用 """...""" 说明函数用途
4. 避免副作用:相同的输入要得到相同的输出
5. 善用默认参数:让函数调用更简洁
6. 尽量少用global:用参数和返回值传递数据
▶写在最后
今天的内容有点多,但我相信只要你动手敲一遍代码,就会发现函数其实没那么复杂。
核心就几点:
今天的内容就到这里啦,你学会了吗?有什么不明白的地方,欢迎在评论区打出来,我们可以一起交流。如果你也想入局AI赛道或者有什么好的想法,欢迎来找我。
我会给你提供一些基础的AI资料。