【零基础学Python】05-Python函数完全指南:从初阶定义到进阶参数,一篇打通核心难点

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🎯 你正在阅读「Python 从零摸索日记」系列文章 🎯

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🔥 弹简特 个人主页

❄️ 个人专栏直通车:

靠热爱去书写自己,靠勇敢去书写生活!

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨


🌟 博主简介:


文章目录:


一、前言

函数是Python代码复用的基石。本文从零讲解函数定义、调用、形参实参、返回值、作用域,再到位置参数、关键字参数、默认参数及*args/**kwargs,助你写出优雅高效的代码。


二、函数初阶

1、认识函数及其作用

函数 = 把一段代码打包,起个名字,以后随时喊名字就能执行这段代码。

这样做的好处:

  1. 不用重复写:同样的功能,写一次函数,后面直接调用。
  2. 容易修改:要改功能,只改函数里面的代码,所有调用它的地方自动生效。
  3. 逻辑清晰:把大任务拆成小函数,每个函数做一件事。

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 函数名():
    # 缩进的代码块
    第一行代码
    第二行代码
    ...

规则 :函数名必须符合标识符命名规则(字母、数字、下划线,不能数字开头,不能是关键字如 ifforwhile)。

例子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 关键字的作用:

  1. 把值返回到函数调用的地方
  2. 立即结束函数(return后面的代码不执行)。
  3. 返回到调用函数的位置(继续往下执行)。

例子1:简单返回

python 复制代码
def double(x):
    return x * 2

result = double(5)
print(result)   # 10

过程:double(5) 被调用 → 形参 x 变成 5 → 计算 5*2=10return 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

如果没有 globalcounter = 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、参数组合使用总结

调用时的规则(重要)

  1. 位置参数(按顺序给的)必须在前
  2. 关键字参数(形参=值)在后
  3. 不能给同一个形参多次赋值

定义时的规则(重要)

顺序必须是:

复制代码
位置参数 → *args → 默认参数 → **kwargs
  • *args 吸收多余的位置参数
  • **kwargs 吸收多余的关键字参数

快速记忆

*args 是位置参数的口袋(元组),**kwargs 是关键字参数的口袋(字典)。


四、写在最后

能坚持看到最后的朋友,属实是真爱学编程了,先歇会儿眼睛放松一下~

很多同学刚开始学Python,都不知道它到底能干啥。还是简单跟大伙唠唠:

可以做自动化测试 、写办公脚本解放双手爬虫采集数据做数据分析、开发小工具、甚至做简单后端和小游戏,用途真的特别广。

最后分享几句实在又走心的话送给正在学Python的你:

  1. 慢慢来别着急,代码不会辜负每一个愿意坚持敲键盘的人

  2. 现在多学一点技能,未来就多一份选择的底气

  3. 与其原地迷茫焦虑,不如从一行Python代码开始悄悄变强

  4. 看似不起眼的日复一日,终会在将来的某天,让你看到坚持的意义

铁汁们~ 觉得内容有用的话,麻烦点个赞、关注一波,后续会持续更新Python入门、实战、自动化相关笔记,咱们一起慢慢进阶。

文章有哪里讲得不妥的,欢迎评论区随时指正,大家一起交流进步!

兄弟们,咱们一起学好Python,悄悄逆袭~~~

相关推荐
梦想三三1 小时前
【NLP入门到实战】TF-IDF算法详解 + 红楼梦120回关键词提取
人工智能·python·计算机视觉
AugustRed1 小时前
A2UI 完整学习指南(含 Java 后端 + 前端实战示例)
java·开发语言·前端
jingling5551 小时前
自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图
android·开发语言·前端·ai·nextjs
basketball6161 小时前
C++进阶:1. 引用折叠规则
java·开发语言·c++
学地理的小胖砸1 小时前
【批量处理tiff文件生成jpg缩略图】
数据库·人工智能·python
机汇五金_1 小时前
通信设备防雨箱如何兼顾防护与散热?
网络·python
糖果店的幽灵2 小时前
LangChain 1.3 完全教程:从入门到精通-Part 7: Documents(文档处理)
java·python·langchain
Wonderful U2 小时前
基于Python爬虫+Django的轻量化天气预报系统:从数据抓取到可视化展示的完整实战
爬虫·python·django
Deep-w2 小时前
【MATLAB】基于模型预测控制的自适应巡航车辆过渡工况安全控制研究
开发语言·人工智能·算法·机器学习·matlab