✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🎯 你正在阅读「Python 从零摸索日记」系列文章 🎯
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🔥 弹简特 个人主页
❄️ 个人专栏直通车:
✨ 靠热爱去书写自己,靠勇敢去书写生活!
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
🌟 博主简介:

文章目录:
- 一、前言
- 二、函数初阶
-
- 1、认识函数及其作用
- 2、函数的两种类型
-
- [2.1 内建函数](#2.1 内建函数)
- [2.2 自定义函数(自己造)](#2.2 自定义函数(自己造))
- 3、函数的定义和调用
- 4、形参和实参
- 5、函数的返回值
-
- [`return` 关键字的作用:](#
return关键字的作用:)
- [`return` 关键字的作用:](#
- [6、作用域(全局变量 vs 局部变量)](#6、作用域(全局变量 vs 局部变量))
-
- [6.1 全局变量](#6.1 全局变量)
- [6.2 局部变量](#6.2 局部变量)
- [6.3 全局和局部重名时](#6.3 全局和局部重名时)
- [6.4 如何在函数内部修改全局变量?](#6.4 如何在函数内部修改全局变量?)
- 三、函数进阶
-
- 1、位置参数
- 2、关键字参数
- 3、默认参数
- [4、 不定长位置参数(`*args`)](#4、 不定长位置参数(
*args)) - 5、不定长关键字参数(`**kwargs`)
- 6、参数组合使用总结
- 四、写在最后
一、前言
函数是Python代码复用的基石。本文从零讲解函数定义、调用、形参实参、返回值、作用域,再到位置参数、关键字参数、默认参数及*args/**kwargs,助你写出优雅高效的代码。
二、函数初阶
1、认识函数及其作用
函数 = 把一段代码打包,起个名字,以后随时喊名字就能执行这段代码。
这样做的好处:
- 不用重复写:同样的功能,写一次函数,后面直接调用。
- 容易修改:要改功能,只改函数里面的代码,所有调用它的地方自动生效。
- 逻辑清晰:把大任务拆成小函数,每个函数做一件事。
2、函数的两种类型
Python里有两种函数:内建函数 和 自定义函数。
2.1 内建函数
这些是Python已经造好的"工具",你不需要知道里面怎么实现的,直接拿来用。
比如我们之前就接触过的内建函数如下:
| 函数 | 作用 | 例子 |
|---|---|---|
print() |
输出内容到屏幕 | print("你好") → 显示:你好 |
input() |
等待用户输入,返回字符串 | name = input("请输入名字:") |
int() |
把东西转成整数 | int("123") → 123 |
str() |
转成字符串 | str(456) → "456" |
float() |
转成小数 | float("3.14") → 3.14 |
type() |
查看数据类型 | type(10) → <class 'int'> |
len() |
返回长度(字符数、元素个数) | len("hello") → 5 |
range() |
生成一个整数序列,常用于循环 | for i in range(3): 循环0,1,2 |
注意 :eval()它能把字符串当成Python代码执行,比如 eval("1+2") 会得到3。但新手先少用。
练习一下:
python
print(len("Python"))
print(int("99") + 1)
print(type(3.14))

2.2 自定义函数(自己造)
当内建函数不够用时,你自己用 def 关键字定义。
基本格式:
python
def 函数名():
# 缩进的代码块
第一行代码
第二行代码
...

规则 :函数名必须符合标识符命名规则(字母、数字、下划线,不能数字开头,不能是关键字如 if、for、while)。
例子1:最简单的函数
python
def say_hi():
print("你好呀")
print("欢迎学习Python")
# 定义后不会自动执行,必须调用
say_hi()
输出:

例子2:多个语句
python
def cook_rice():
print("洗米")
print("加水")
print("按煮饭键")
print("等待")
cook_rice()
关键点 :函数必须先定义,后调用。如果把调用 cook_rice() 写在定义 def 前面,会报错 NameError。如下所示:
py
cook_rice() # NameError: name 'cook_rice' is not defined
def cook_rice():
print('洗米')
print('加水')
print('按煮饭键')
print('等待')

3、函数的定义和调用
很多新手分不清"定义"和"调用",我用生活例子帮你区分。
-
定义 :相当于你写了一个菜谱,写好了放抽屉里。菜谱本身不会自动做菜。类比就是我们定义了一个函数,那么就会将这段代码加载到内存放好,但是没有执行。

-
调用 :你拿出菜谱,说"按照这个做",才开始做菜。类比就是你调用函数,那么我就去内存里面找到对应的函数,然后执行

-
之所以有上述的场景,所以我们的函数调用不能写在定义之前,因为你调用是去内存中找到对应函数然后执行,而我们只有使用def关键字定义出函数,我们的函数才会被放到内存中,如果你在def之前也就是函数没定义你就去调用的话,那么就会出现你在内存中找不到该函数,而报一个函数未定义的错误。
代码里:
python
# 定义(写菜谱)
def wash_clothes():
print("放洗衣液")
print("按启动")
# 调用(真正洗衣服)
wash_clothes()
如果你只定义不调用,什么都不会发生。
我们在举一个例子,说明为什么函数可以减少代码冗余,增加代码的复用。
比如我们现在要在每个月的1号、15号、30号都会唱歌
如果不使用函数,代码如下:
py
# 案例:我们每个月1号、15号、30号都会唱歌
while True: # 死循环,输入0退出
day = int(input('请输入今天是多少号:'))
if day == 1:
print('一闪一闪亮晶晶')
print('满天都是小星星')
elif day == 15:
print('一闪一闪亮晶晶')
print('满天都是小星星')
elif day == 30:
print('一闪一闪亮晶晶')
print('满天都是小星星')
elif day == 0:
# 如果你输入的是0,那么就退出循环
print('不参加唱歌')
break
else:
print('今天不唱歌~~')
结果:

分析问题:
- 代码冗余

- 维护不方便

优化后的例子:
python
# 先定义函数
def sing():
print('一闪一闪亮晶晶')
print('满天都是小星星')
while True: # 死循环,输入0退出
day = int(input('请输入今天是多少号:'))
if day == 1:
sing()
elif day == 15:
sing()
elif day == 30:
sing()
elif day == 0:
# 如果你输入的是0,那么就退出循环
print('不参加唱歌')
break
else:
print('今天不唱歌~~')

4、形参和实参
有些函数需要"输入"才能工作,比如"向某个人问好",你得告诉函数那个人的名字。
- 形参 (形式参数):定义函数时写在括号里的变量名,相当于一个占位符,代表"将来这里会传入一个值"。
- 实参(实际参数):调用函数时实际传入的具体值。
例子1:一个参数
python
# 定义:name 是形参
def greet(name):
print(f"你好,{name},今天天气不错")
# 调用:"小明" 是实参
greet("小明")
greet("小红")
输出:

例子2:两个参数
python
def add(a, b):
result = a + b
print(f"{a} + {b} = {result}")
add(3, 5)
add(10, 20)
输出:

重要规则:
-
实参的数量必须和形参一样多(不能多也不能少)。


-
实参的顺序和形参一一对应(第一个实参给第一个形参,以此类推)。

错误示范:
python
def test(x, y):
pass
test(1) # 缺少一个实参 → TypeError
test(1, 2, 3) # 多了一个实参 → TypeError
生活类比 :
你去复印店,老板说"你要复印几份?"(形参是"份数"),你说"5份"(实参是5)。如果你不说份数,或者说了"你好",老板都蒙了。
5、函数的返回值
函数处理完数据后,经常需要把结果传回给调用它的地方。这就像你去食堂打饭,你把饭卡给阿姨(传参数),阿姨打好饭把餐盘递还给你(返回值)。
return 关键字的作用:
- 把值返回到函数调用的地方。
- 立即结束函数(return后面的代码不执行)。
- 返回到调用函数的位置(继续往下执行)。
例子1:简单返回
python
def double(x):
return x * 2
result = double(5)
print(result) # 10
过程:double(5) 被调用 → 形参 x 变成 5 → 计算 5*2=10 → return 10 →返回到调用函数的那一行代码将 这个返回值10被赋值给 result。

例子2:没有 return 会怎样?
python
def add(a, b):
c = a + b
# 没有 return
value = add(3, 4)
print(value) # 输出 None
没有 return 的函数,默认返回 None(Python里的"空")。

例子3:return 后面的代码不执行
python
def test():
print("第一行")
return
print("第二行") # 永远不会执行
test() # 只打印"第一行"

例子4:返回多个值(Python特有,可以一次返回多个,用逗号隔开,外面用多个变量接收)
python
def get_name_and_age():
return "张三", 25
name, age = get_name_and_age()
print(name) # 张三
print(age) # 25

6、作用域(全局变量 vs 局部变量)
作用域 = 变量的"可见范围",即在哪里能访问这个变量。
6.1 全局变量
定义在所有函数之外的变量,从定义的位置开始,到文件结束,任何地方都可以访问。
python
# 全局变量
school = "清华"
def func1():
print("func1 里访问 school:", school)
def func2():
print("func2 里访问 school:", school)
func1()
func2()
print("外面也能访问:", school)
全部都能打印出"清华"。

6.2 局部变量
定义在函数内部的变量,只能在该函数内部使用。函数执行完毕后,局部变量就被销毁了,外面访问不到。
python
def func():
x = 10 # 局部变量
print("函数内部 x =", x)
func()
print("函数外部尝试打印 x:", x) # 报错 NameError: name 'x' is not defined

为什么要有局部变量?
为了避免不同函数之间的变量互相干扰。比如两个函数都用 i 做循环变量,互不影响。
6.3 全局和局部重名时
如果函数内部定义了一个和全局变量名字相同 的变量,那么函数内部优先使用局部变量 ,并且不会修改全局变量。两者各自独立。其实就是就近原则,我有的话我就使用我的,而且就算我的名字和公共一样,但是我修改我自己的不影响公共的,他们是独立的
python
name = "刘亦菲" # 全局
def show():
name = "赵丽颖" # 局部,和全局同名
print("函数内部 name =", name)
show()
print("函数外部 name =", name)
输出:

注意 :Python在函数内部看到 name = ... 时,会默认创建一个新的局部变量,而不是去修改全局的那个。这是为了保护全局变量不被意外修改。
6.4 如何在函数内部修改全局变量?
使用 global 关键字声明这个变量是全局的。
python
counter = 0 # 全局
def increment():
global counter # 声明我要用全局的 counter
counter = counter + 1
increment()
increment()
print(counter) # 2

如果没有 global,counter = counter + 1 会报错(因为右边 counter 还没定义,Python认为这是局部变量,但局部变量在赋值前被引用了)。
规则:
- 如果只是读取 全局变量的值,不需要
global。 - 如果要修改 全局变量的值(赋值),必须先用
global声明。
python
total = 100
def read_only():
print(total) # 读取,没问题
def modify():
global total
total = 200 # 修改,需要 global
三、函数进阶
1、位置参数
一句话解释 :
调用函数时,你给的实参会按照从左到右的顺序,依次赋给形参。个数必须和形参一样多。
特点:
- 实参与形参一一对应,数量相等
- 少了会报错(missing required positional argument)
- 多了也会报错(takes X positional arguments but Y were given)
简单例子:
python
def introduce(name, age, city):
print(f"我叫{name},今年{age}岁,住在{city}")
# 正确调用:三个实参,顺序对应
introduce("小明", 18, "北京")
# 错误调用1:少一个
# introduce("小明", 18) # 报错,缺少city
# 错误调用2:多一个
# introduce("小明", 18, "北京", "程序员") # 报错,多了一个
正确调用:

少一个:

多一个:

类比 :
就像填表格,表格有三个空(姓名、年龄、城市),你必须按顺序填三个内容,不能多也不能少。
2、关键字参数
一句话解释 :
在调用函数时,用"形参名=值"的方式指定实参,这样可以不按照位置顺序传递,更清晰。
特点:
- 调用时写
形参名=实参值 - 可以打乱顺序,Python会根据形参名字去匹配
- 可以和位置参数混用,但位置参数必须在关键字参数之前
- 每个形参只能接收一个实参,不能重复给同一个形参
例子1:纯关键字参数(顺序随意)
python
def show_info(name, age, score):
print(f"姓名:{name},年龄:{age},成绩:{score}")
# 正常位置调用
show_info("小红", 20, 95)
# 使用关键字参数,顺序可以调换
show_info(score=95, name="小红", age=20) # 完全没问题


例子2:位置参数和关键字参数混合
python
def test(a, b, c):
print(a, b, c)
# 混合使用:位置参数在前,关键字参数在后
test(1, 2, c=3) # 正确:a=1, b=2, c=3
test(1, c=3, b=2) # 正确:a=1, b=2, c=3,关键字参数顺序无所谓
test(a=1, 2, 3) # 错误!SyntaxError: positional argument follows keyword argument
# 一旦出现关键字参数,后面就不能再跟位置参数了


例子3:重复赋值(错误)
python
def func(x, y):
print(x, y)
# func(1, x=2) # 错误:x接收了两个值(1和2),TypeError

为什么要用关键字参数?
- 当参数很多时,很容易搞乱顺序,用关键字参数能明确每个值对应哪个参数。
- 可以跳过某些默认参数(下一节学),只修改你关心的参数。
3、默认参数
一句话解释 :
定义函数时,给形参直接赋一个默认值。调用时,如果不给这个参数传值,就使用默认值;如果传了,就覆盖默认值。
语法 :def 函数名(位置参数, 形参=默认值):
规则:
- 默认参数必须放在所有位置参数之后,不能混在前面。
- 可以有多个默认参数。
- 调用时,可以只传递部分实参,未传的会用默认值。
例子1:基本使用
python
def greet(name, greeting="你好"):
print(f"{greeting},{name}")
greet("小明") # 输出:你好,小明(使用默认greeting)
greet("小红", "哈喽") # 输出:哈喽,小红(覆盖默认值)
例子2:多个默认参数
python
def register(name, age=18, city="未知"):
print(f"姓名:{name},年龄:{age},城市:{city}")
register("张三") # 年龄18,城市未知
register("李四", 25) # 年龄25,城市未知
register("王五", city="上海") # 年龄18,城市上海(使用关键字参数跳过age)
register("赵六", 30, "北京") # 全部指定
例子3:错误示范(默认参数不能在前)
python
# def bad_func(gender="男", name): # SyntaxError,默认参数不能在非默认参数前面
# pass

常见坑(重要) :
默认参数的值在函数定义时 就计算好了,而不是每次调用时。如果你用可变对象(如列表、字典)作为默认值,可能会出问题。但你是0基础,先记住:默认参数尽量用整数、字符串、None这些不可变类型 ,暂时避免用空列表 []。
使用场景:
- 你想让函数大部分情况使用某个值,但偶尔需要改变。比如日志级别默认INFO,调试时可改成DEBUG。
- 向后兼容:旧代码调用函数时没传新参数,新参数有默认值,不会报错。
4、 不定长位置参数(*args)
一句话解释 :
如果你不确定调用时会传入多少个位置参数 (可以0个,也可以无数个),就用 *args 来接收。所有的额外位置参数会被打包成一个元组(tuple)。
语法 :def 函数名(*args):
名字不是固定的 :星号后面的名字可以是任意合法变量名,但习惯用 args。
例子1:接收任意多个数字求和
python
def sum_all(*args):
total = 0
for num in args:
total += num
return total
print(sum_all(1, 2)) # 3
print(sum_all(1, 2, 3, 4)) # 10
print(sum_all()) # 0(没有报错,空元组)

例子2:查看类型和内容
python
def test(*args):
print("args是:", args)
print("类型:", type(args))
test(10, 20, 30) # 输出:args是: (10, 20, 30) ,类型: <class 'tuple'>
test() # 输出:args是: () ,类型: <class 'tuple'>

例子3:和普通位置参数配合 (*args 要放在最后)
python
def show(first, second, *others):
print("前两个固定参数:", first, second)
print("剩余的参数:", others)
show(1, 2, 3, 4, 5)
# 输出:
# 前两个固定参数: 1 2
# 剩余的参数: (3, 4, 5)


注意事项:
*args会吸收所有多余的位置参数。- 一个函数只能有一个
*args。 - 如果没有多余的位置参数,
args就是一个空元组。
5、不定长关键字参数(**kwargs)
一句话解释 :
如果你不确定调用时会传入多少个关键字参数 (如 name="张三"、age=20),就用 **kwargs 来接收。所有额外的关键字参数会被打包成一个字典(dict),键是参数名,值是参数值。
**kwargs 中的 kwargs 只是约定俗成的变量名,你可以换成任何合法的名字,比如 **info、**options、**params。
关键的是那两个星号 **,它告诉 Python:把传入的所有未明确定义的关键字参数打包成一个字典。因为字典是是键值对形式,有键也有值,所以才有两个*。
语法 :def 函数名(**kwargs):
例子1:接收任意关键字参数并打印
python
def show_info(**kwargs):
print("接收到的关键字参数:", kwargs)
for key, value in kwargs.items():
print(f"{key} = {value}")
show_info(name="小明", age=18, city="北京")
# 输出:
# 接收到的关键字参数: {'name': '小明', 'age': 18, 'city': '北京'}
# name = 小明
# age = 18
# city = 北京
show_info() # 不报错,输出空字典 {}

例子2:配合普通参数和 *args(顺序很重要)
完整顺序:位置参数 → *args → 默认参数 → **kwargs
乱序会直接报语法错误
即:先位置再默认后关键字:位置,不定长位置,默认,不定长关键字
python
def super_func(a, b, *args, c=100, **kwargs):
print("a:", a, "b:", b)
print("args:", args)
print("c:", c)
print("kwargs:", kwargs)
super_func(1, 2, 3, 4, 5, c=200, name="x", age=18)
# 输出:
# a: 1 b: 2
# args: (3, 4, 5)
# c: 200
# kwargs: {'name': 'x', 'age': 18}

例子3:常见错误------**kwargs 必须在最后,且只能有一个
python
# def wrong_func(**a, **b): # 错误,两个**kwargs
# pass
# def wrong_func2(**kwargs, x): # 错误,**kwargs后面不能再有位置参数
# pass
为什么要用 **kwargs?
- 编写装饰器、框架、库时,需要把额外参数传给其他函数。
- 当你需要接受用户任意配置项时(比如
settings(debug=True, level="INFO", ...))。
6、参数组合使用总结
调用时的规则(重要):
- 位置参数(按顺序给的)必须在前
- 关键字参数(
形参=值)在后 - 不能给同一个形参多次赋值
定义时的规则(重要) :
顺序必须是:
位置参数 → *args → 默认参数 → **kwargs
*args吸收多余的位置参数**kwargs吸收多余的关键字参数
快速记忆 :
*args 是位置参数的口袋(元组),**kwargs 是关键字参数的口袋(字典)。
四、写在最后
能坚持看到最后的朋友,属实是真爱学编程了,先歇会儿眼睛放松一下~
很多同学刚开始学Python,都不知道它到底能干啥。还是简单跟大伙唠唠:
可以做自动化测试 、写办公脚本解放双手 、爬虫采集数据 、做数据分析、开发小工具、甚至做简单后端和小游戏,用途真的特别广。
最后分享几句实在又走心的话送给正在学Python的你:
慢慢来别着急,代码不会辜负每一个愿意坚持敲键盘的人。
现在多学一点技能,未来就多一份选择的底气。
与其原地迷茫焦虑,不如从一行Python代码开始悄悄变强。
看似不起眼的日复一日,终会在将来的某天,让你看到坚持的意义。
铁汁们~ 觉得内容有用的话,麻烦点个赞、关注一波,后续会持续更新Python入门、实战、自动化相关笔记,咱们一起慢慢进阶。
文章有哪里讲得不妥的,欢迎评论区随时指正,大家一起交流进步!
兄弟们,咱们一起学好Python,悄悄逆袭~~~

