Appearance
函数
写到第三个"把文章标题和作者拼成一行"的逻辑时,重复就明显了——同样的 f"{title} —— {author}" 在好几个地方出现,改一处忘改另一处,bug 就来了。函数就是给这种重复逻辑起个名字,之后调用名字就行,逻辑只写一遍。
一、定义函数
python
def format_article(title, author):
"""把文章标题和作者拼成一行展示文本。"""
return f"{title} —— {author}"
line = format_article("Python 入门", "张三")
print(line) # Python 入门 —— 张三def 后面是函数名,括号里 title、author 这些是占位的参数,调用时填进去真实值。return 把结果交回给调用方。
函数名和变量名一样,小写加下划线——format_article 比 fmtArt 多打几个字,但过几个月再打开,前者一眼就懂,后者得猜半天。
二、参数
1 默认参数
有些参数大部分时候取同一个值,可以设默认,调用时就不必每次都写:
python
def format_article(title, author, date="未知"):
return f"{title} —— {author}({date})"
print(format_article("Python 入门", "张三"))
# Python 入门 —— 张三(未知)
print(format_article("Go 并发", "李四", "2024-06-01"))
# Go 并发 —— 李四(2024-06-01)date 不传就用默认值。默认参数要放在参数列表末尾——先写必填的,再写带默认的,顺序反了 Python 直接报错。
2 关键字参数
参数一多,光靠位置传值容易记错顺序。调用时显式写参数名,顺序就无所谓了:
python
def create_contact(name, phone, email="未填写"):
return {"name": name, "phone": phone, "email": email}
contact = create_contact(phone="13800138000", name="王五")
print(contact)
# {'name': '王五', 'phone': '13800138000', 'email': '未填写'}phone 和 name 的传入顺序跟定义里相反也没事,因为带了名字。这种写法在参数四五个的时候特别值——加一个新参数不会把老调用的位置全打乱。
3 可变参数
不确定会传多少个值时,*args 把它们收成一个元组:
python
def join_tags(*tags):
"""把多个标签拼成一个字符串,逗号分隔。"""
return ", ".join(tags)
print(join_tags("Python", "入门", "编程"))
# Python, 入门, 编程*tags 把三个字符串收进元组 ("Python", "入门", "编程"),函数里当普通元组用。
**kwargs 收的是关键字参数,成字典:
python
def build_filter(**conditions):
"""把多个筛选条件拼成一行描述。"""
parts = [f"{key}={value}" for key, value in conditions.items()]
return "筛选:" + " AND ".join(parts)
print(build_filter(status="已发布", category="技术"))
# 筛选:status=已发布 AND category=技术status="已发布"、category="技术" 被收进字典 {"status": "已发布", "category": "技术"}。*args 和 **kwargs 这两个名字是约定俗成,* 和 ** 才是关键——叫 *items、**opts 也行。
三、返回值
函数执行完可以返回结果。没写 return,或者只写 return 不带值,返回的就是 None:
python
def log_message(msg):
print(f"[LOG] {msg}")
# 没有 return,返回 None
result = log_message("保存成功")
print(result) # None返回多个值其实就是返回一个元组,调用方拿多个变量接,Python 自动拆开:
python
def parse_date(date_str):
"""把 '2024-06-01' 拆成年、月、日三个整数。"""
year, month, day = date_str.split("-")
return int(year), int(month), int(day)
y, m, d = parse_date("2024-06-01")
print(y, m, d) # 2024 6 1"返回多个值"这个说法容易让人以为 Python 有什么特殊机制——其实没有,就是返回元组 + 自动拆包。知道这点,后面遇到"函数返回三个值我只接了一个"的情况就不会懵:接到的其实是一个三元组。
四、作用域
函数里定义的变量,外面拿不到。这层隔离就是作用域:
python
def process():
count = 10 # 局部变量
print(f"函数内 count={count}")
process()
print(count) # 报错:NameError: name 'count' is not defined函数里能读外面的全局变量,但改不了——要改得先 global 声明:
python
total = 0
def add(amount):
global total # 声明 total 是外面那个
total += amount
add(5)
add(3)
print(total) # 8global 能不用就别用。函数本来是为了把逻辑隔开,结果又靠全局变量串起来,后面排查"这个变量到底被谁改了"会非常痛苦——几十个函数都摸同一个全局变量,改一个地方不知道影响多大。
五、lambda 表达式
有些逻辑就一行,专门起个函数名又嫌重。lambda 写匿名函数:
python
# 普通 def
def get_title(article):
return article["title"]
# lambda 等价写法
get_title = lambda article: article["title"]
articles = [
{"title": "Python 入门", "author": "张三"},
{"title": "Go 并发", "author": "李四"},
]
titles = [get_title(a) for a in articles]
print(titles) # ['Python 入门', 'Go 并发']lambda article: article["title"] 就是 def get_title(article): return article["title"] 的简写,左边参数、右边返回值。
lambda 适合真正只有一行的简单逻辑。逻辑一复杂——要多个步骤、要赋值、要循环——还是老老实实写 def。lambda 拉长之后,排查的人盯着那一行看半天,反而比 def 更难读。
六、文档字符串
函数开头用三引号写的说明,叫文档字符串(docstring),help() 能读出来:
python
def filter_by_status(articles, status):
"""筛选指定状态的文章。
Args:
articles: 文章列表,每个元素是字典。
status: 目标状态,如 '已发布'。
Returns:
状态匹配的文章列表。
"""
return [a for a in articles if a.get("status") == status]
help(filter_by_status)写法上覆盖三件事就够了:这个函数做什么、参数各是什么、返回什么。不用套复杂模板,几句话把关键信息说清楚——过几个月回来改这个函数,这几句话比重新读一遍函数体省事得多。