今天你 AI 赋能了吗?打开浏览器,在地址栏输入 xuefulu.ai ,按下 Enter 即可访问学府路1号!
2026年10月14日 星期三 10:40:22
常用网址: | | 学府路1号 https://xuefulu.ai | 下载 Python https://xuefulu.ai/Python.html | 下载 TRAE https://xuefulu.ai/Trae.html

一、为什么需要函数

先看一段大家都会写的代码:想跟三个同学打招呼,最直接的办法是抄三遍:

Python
print('你好,小明')
print('你好,小红')
print('你好,小刚')

能跑。但如果要跟全班 40 个同学打招呼,抄 40 遍就太傻了。

真正麻烦的是:如果打招呼的话术要改(比如改成「你好,小明同学,欢迎来上课」),你得回去改 40 处,漏一处就出错。同一段逻辑出现第三遍,就该把它收起来——这就是函数。

把「打招呼」做成一个函数,以后想跟谁打招呼,只要报一下名字就完事:

Python
def greet(name):
    print('你好,' + name)

greet('小明')
greet('小红')
greet('小刚')

输出:

输出
你好,小明
你好,小红
你好,小刚

话术只写在一处,想改改一处。这就是函数最大的好处:一次写好,到处复用。

判断要不要写函数,记住一句话:同一段代码你要抄第二遍,就值得写成函数。后面无论算平均分、算面积、判奇偶,套路都一样。

函数能帮我们做什么

场景没有函数有函数
打招呼抄 N 遍 print写一次 greet,调用 N 次
算平均分每换一份成绩单重写一遍累加写一次 average,传不同列表
判奇偶每处都写 if n%2==0写一次 is_even,到处用

下面三件事,就是函数的全部:怎么定义、怎么调用、怎么把外面的值传进去、把里面的值拿回来。

课程思政|工匠精神与严谨求实 · 写第三遍,就该收起来

同一段「打招呼」的话,你抄了三遍,要改的时候就得回去改三处,漏一处就出错。函数的价值就在这儿:把重复的逻辑收成一个按钮,改只改一处。这不只是省事,是怕自己出错。以后无论是算平均分还是写报表,看到自己开始复制粘贴,就该警觉——那是代码在提醒你,该动手收拾一下了。

二、定义与调用

函数长这样,记住四个零件:

示意
# 示意:把「函数名」「参数」「要做的事」换成真实的
def 函数名(参数):
    要做的事(记得缩进)
    可以写很多行
    最后用 return 把结果交出来(也可以不交)

这是长相,不是能直接跑的代码——函数名、参数 还没定义呢。看一个真能跑的:

Python
def greet(name):
    print('你好,' + name)

greet('小明')

输出 你好,小明。

零件 1:def 关键字

def 是英文 define(定义)的缩写,告诉 Python「我要定义一个函数了」。少了它就不是函数。

零件 2:函数名

起名规则和变量一样:字母、数字、下划线,不能以数字开头,不能用关键字(def、if 等)。建议见名知意:greet、area、is_even。

Python
def 1func():        # 报错:数字不能开头
    print('hi')
报错
SyntaxError: invalid decimal literal

零件 3:括号和冒号

函数名后面必须跟 ( 和 ),哪怕没有参数也要写空括号 ();这一行结尾必须有英文冒号 :。

Python
def greet(name)      # 报错:漏了冒号
    print(name)
报错
SyntaxError: expected ':'

零件 4:缩进的函数体

冒号下面、缩进四格的那几行,才是函数真正要做的事。缩进错了,Python 不知道哪些代码属于函数。

必须先定义,后调用

函数要先 def 定义好,才能 greet(...) 调用。调用写在定义前面会找不到它:

Python
print(greet('小明'))   # 报错:greet 还没定义
def greet(name):
    return '你好,' + name
报错
NameError: name 'greet' is not defined
一个常见的动作顺序:先写 def 把函数「做出来」,再在下面写 greet(...) 把它「用起来」。Python 是从上往下读的,读到调用那行时,必须已经见过这个函数的定义。

调用时别忘了括号

很多人写 greet 想让它运行,却忘了加 ()。不加括号,Python 只把「函数本身」给你,不会真的执行:

Python
def greet(name):
    return '你好,' + name

print(greet)      # 少了括号:打印的是函数对象,不是结果
print(greet('小明'))

输出:

输出
<function greet at 0x...>
你好,小明

第一行那串 0x... 是函数在内存里的地址,说明你拿到的是「函数这个东西」,而不是它的运行结果。想运行,一定加上 ()。

三、参数:位置、默认、关键字

函数名后面括号里的 name 叫参数(也叫形参)。调用时传进去的 '小明' 叫实参。参数让函数能处理不同的情况。

位置参数:按先后对号入座

调用时给的值,按顺序一一对应括号里的参数:

Python
def calc(a, b, op):
    if op == '+':
        return a + b
    if op == '-':
        return a - b

print(calc(6, 3, '+'))
print(calc(6, 3, '-'))

输出 9、3。

位置参数个数必须对齐。少传一个或多传一个都会报错:
Python
def add(a, b):
    return a + b

print(add(1))          # 少传一个
报错
TypeError: add() missing 1 required positional argument: 'b'
Python
def add(a, b):
    return a + b

print(add(1, 2, 3))    # 多传一个
报错
TypeError: add() takes 2 positional arguments but 3 were given

默认值参数:可传可不传

在定义时给参数写上 = 默认值,调用时就可以不传,自动用默认值:

Python
def greet(name='同学'):
    return '你好,' + name

print(greet())
print(greet('小明'))

输出:

输出
你好,同学
你好,小明

规则:有默认值的参数必须排在「没有默认值」的参数后面。下面这样写会报错:

Python
def greet(name='同学', age):   # 报错:默认值参数不能在前面
    return name
报错
SyntaxError: parameter without a default follows parameter with a default

关键字参数:指名道姓地传

调用时写成 参数名=值,就不必按位置排,还能让代码更好懂:

Python
def greet(name, age):
    return name + '今年' + str(age) + '岁'

print(greet(age=18, name='小明'))

输出 小明今年18岁。注意关键字调用时顺序可以打乱,但名字必须和定义里的参数名一致:

Python
def greet(name, age):
    return name

print(greet(n='小明', age=18))   # 报错:没有叫 n 的参数
报错
TypeError: greet() got an unexpected keyword argument 'n'
三种用法怎么选:大多数情况用位置参数就够了;某个参数经常取同一个值(比如分隔符),就给它默认值;参数一多、容易搞混顺序,调用时就用关键字参数写清楚。

四、返回值 return

return 的意思是「把结果交出去」。没有它,函数干完活也什么都没带回来。

没有 return,得到的是 None

Python
def f():
    x = 1

def g():
    return 1 + 2

print(f())
print(g())

输出:

输出
None
3

None 表示「什么都没有」。函数里只算了 x=1 却没 return,调用结果就是 None。

return 之后,后面的代码不执行

函数一旦碰到 return 就立刻收工,后面写什么都不会跑:

Python
def f():
    return 1
    print('这行永远不会执行')

print(f())

输出只有 1,那句 print 根本没机会运行。

常常有人把「想打印结果」写成 return 以为会自动显示——不会。return 只是把值交回给调用处,要看到结果还得在调用处 print。

返回多个值,其实是返回一个元组

写成 return a, b, c,Python 会把它们打包成一个元组交回来:

Python
def fx():
    return 1, 2, 3

print(fx())
print(type(fx()))

输出:

输出
(1, 2, 3)
<class 'tuple'>

所以「返回多个值」不是特殊语法,就是返回了一个元组。你也可以用多个变量一次接住:

Python
a, b, c = fx()
print(a, b, c)

输出 1 2 3。

return 是「把结果交给别人用」,print 是「在屏幕上显示给人看」。如果后面还要拿这个结果继续算,一定要用 return,别用 print。

课程思政|诚信与职业操守 · 交出去,才算做完

函数算了半天,没有 return,外面拿到的还是 None——活儿干了,成果却没交出去。这很像做事:自己心里「做完了」不算数,把手里的结果实实在在交到该接的人手上,一件事才算闭环。团队里最怕的就是「我以为他做了」,所以该交的值、该报的数,别留在自己这儿,明确地交出去。

五、print 和 return 的区别

这是新手最容易混的一对。一句话记住:print 是给人看的,return 是给程序用的。

print(x)return x
作用把 x 显示在屏幕上把 x 交回调用处,存进变量
之后还能算吗不能,只是显示了能,y = f() 拿到 x 继续算
不接住会怎样屏幕上多一行字得到 None

看这个对比,差别一目了然:

Python
def f():
    print('hi')

x = f()
print('x =', x)

输出:

Python
hi
x = None

f() 只打印了 hi,但没有 return,所以 x 拿到的是 None。

Python
def g():
    return 'hi'

y = g()
print('y =', y)

输出 y = hi。g() 把 'hi' 交回来,y 才真正拿到了它。

一个常见的错误

想算平均分却用 print,结果拿不到数:

Python
def average(scores):
    print(sum(scores) / len(scores))   # 只打印,没 return

a = average([88, 92, 75])
print('及格线以上:', a + 10)          # 报错:None 不能加 10
报错
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

average 没 return,a 是 None,None + 10 直接报错。把 print(...) 改成 return ... 就正常了。

记忆口诀:「要显示给人看」用 print,「要交给下一步算」用 return。需要把结果存起来、传下去,永远选 return。

六、变量作用域

函数里面定义的变量,外面一般看不到。这叫作用域。

局部变量:函数里的「私人物品」

Python
def fx():
    x = 10        # x 只在 fx 里面有效
    print(x)

fx()
print(x)         # 报错:外面的世界没有 x
报错
NameError: name 'x' is not defined. Did you mean: 'fx'?

全局变量:函数外面定义的

函数外面定义的变量,函数可以读,但不能直接改:

Python
count = 0

def inc():
    count = count + 1   # 报错:想改全局,却没说 global

inc()
报错
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Python 看到函数里给 count 赋值,就以为你要建一个局部的 count,结果右边的 count + 1 找不到值,报错。

想改全局,要写 global

Python
count = 0

def inc():
    global count       # 声明:我要用的是外面的那个 count
    count = count + 1

inc()
print(count)

输出 1。加上 global count,函数里改的就是外面的全局变量了。

新手尽量少用全局变量。需要什么值,就用参数传进去;要拿出什么结果,就用 return 交出来。global 用多了,代码会变得很难看懂,也容易改错。
判断一个变量能不能改,先看它在哪定义:函数外的能读、改要 global;函数内的只在函数内有效。最省心的写法永远是「传进去、return 出来」。

AI 助手小贴士|作用域规则里藏着一个经典对比,让 AI 摆出来。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:为什么函数里改外面的列表不用 global 声明、重新赋值一个 int 却要?给两个对照的小例子讲清楚

课程思政|数据安全与法治素养 · 各管各的边界

函数里的变量是「私人物品」,外面看不见;外面的变量函数能读、却默认改不了,要改还得专门写 global。这套规矩不是多余的,它划的就是边界。谁能看见、谁能改动,事先说清楚,系统才不乱。做数据的人对这条尤其要敏感:不同岗位、不同权限能碰到的东西本就该不一样,越界读写一次,信任就少一分。

七、函数的嵌套:函数里再定义函数

函数的定义可以写在另一个函数里面,这叫嵌套函数。

Python
def outer():
    def inner():
        print('我是内层函数')

    inner()        # 在外层里调用它

outer()

关键点:内层函数只在外层函数「被执行」的时候才被定义,出了外层就找不到了。

Python
def outer():
    def inner():
        print('你好')

outer()
inner()      # 在外层外面调用 → 找不到

运行后会报错(逐字照抄解释器):

报错
NameError: name 'inner' is not defined. Did you mean: 'iter'?

内层函数能看到外层的变量

Python
def outer():
    msg = '你好'

    def inner():
        print(msg)      # 直接用了外层的变量

    inner()

outer()

输出:

输出
你好

不用写任何关键字,内层函数就能读到外层的变量。但要在内层修改它,就需要 nonlocal 了——这正是下一节要讲的。

嵌套有什么用:把「只有自己用得到」的辅助逻辑藏起来

Python
def average(scores):
    def clean(nums):              # 只给 average 用的小工具
        return [n for n in nums if n >= 0]

    data = clean(scores)
    return round(sum(data) / len(data), 1)

print(average([90, -1, 80, 100]))

输出:

输出
90.0

clean 这个名字只存在于 average 内部,不会跑到外面去占用名字——这正是嵌套的第一个好处:辅助函数不外泄。

普通的全局函数嵌套函数
定义在哪最外层另一个函数内部
谁能调用任何地方只有外层函数内部
能不能看到外层变量看不到能看到(读没问题)
典型用途给很多地方复用只服务外层那一小段逻辑
判断要不要用嵌套:这段逻辑是不是「只为当前这个函数服务、别处根本用不到」?是就藏进去,不是就提到外面当普通函数。

八、闭包:函数「记住」了外面的变量

如果外层函数不直接调用内层函数,而是把它当成结果返回出去,奇妙的事情就发生了:内层函数会记住外层函数的变量,哪怕外层函数早就执行完了。

Python
def make_adder(n):
    def add(x):
        return x + n        # 用到了外层的 n
    return add              # 把内层函数返回出去

add5 = make_adder(5)
add10 = make_adder(10)
print(add5(1))
print(add10(1))
print(add5(100))

输出:

输出
6
11
105

make_adder(5) 返回的 add5 一直记着 n = 5;add10 记着 n = 10。这两个函数各自带着自己那份数据,互不干扰。

这种「内层函数 + 它记住的外层变量」的组合,就叫闭包(closure)。

经典用途:不开全局变量也能存状态

就像奶茶店的集点卡:每张卡各记各的杯数,互不串账——每个闭包也各存各的 count。

Python
def make_counter():
    count = 0

    def step():
        nonlocal count      # 我要改的是外层的 count
        count += 1
        return count

    return step

c = make_counter()
print(c())
print(c())
print(c())

输出:

输出
1
2
3

nonlocal count 的意思是:「我要改的是外层函数里的那个 count,不要在函数里新建一个」。只读可以不写,要改就必须写——忘写就会悄悄新建一个局部变量,然后报错:

Python
def make_counter():
    count = 0

    def step():
        count = count + 1    # 忘了 nonlocal
        return count

    return step

c = make_counter()
print(c())

运行后会报错(逐字照抄解释器):

报错
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

三个作用域关键字,一次分清

写法作用用在哪
什么都不写只读外层变量没问题;一旦赋值就新建局部变量绝大多数情况
global x声明「我要用并修改全局的那个 x」改全局变量时
nonlocal x声明「我要用并修改外层函数的那个 x」闭包里改外层变量时

每个闭包各存各的

Python
def make_counter():
    count = 0
    def step():
        nonlocal count
        count += 1
        return count
    return step

c1 = make_counter()
c2 = make_counter()
c1()
c1()
print('c1:', c1())
print('c2:', c2())

输出:

Python
c1: 3
c2: 1
闭包「记住」的是变量本身,不是赋值那一刻的值。所以在循环里造闭包要留神——几个函数可能都指向同一个变量,最后都拿到最后一个值:
Python
funcs = []
for i in range(3):
    def f():
        return i
    funcs.append(f)

print([f() for f in funcs])    # 全是 2,不是 0 1 2

输出:

输出
[2, 2, 2]
用一句话总结闭包:函数被带出去之后,还能用当初定义它的那个环境里的变量。它的价值是把状态藏在函数里,不用开全局变量,多个闭包之间也不会互相串。

九、参数进阶 *args/**kwargs

有时你不知道要收几个参数,比如「把所有分数加起来」。这时候用 *args 和 **kwargs。

*args:收下任意多个位置参数

前面加一个星号,所有位置参数会被装进一个元组:

Python
def f(*args):
    print(args)

f(1, 2, 3)
f(1)

输出:

输出
(1, 2, 3)
(1,)

**kwargs:收下任意多个关键字参数

前面加两个星号,所有 名字=值 会被装进一个字典:

Python
def f(**kwargs):
    print(kwargs)

f(a=1, b=2)

输出 {'a': 1, 'b': 2}。

参数顺序口诀

一个函数里几种参数同时出现,顺序固定为:普通参数 → 默认值参数 → *args → **kwargs。

Python
def f(a, b=0, *args, **kwargs):
    print(a, b, args, kwargs)

f(1, 2, 3, 4, c=5)

输出 1 2 (3, 4) {'c': 5}。1 给 a,2 给 b(默认值被覆盖),3,4 进 args,c=5 进 kwargs。

顺序写反会直接报 SyntaxError。记不住就背这句口诀:普通 → 默认 → *args → **kwargs,从左到右越来越「宽」。起步阶段用得少,先认识长相即可。
*args / **kwargs 常用于「写工具函数」或「不确定收多少参数」的场景。日常自己写函数,先老老实实用普通参数,需要了再用它们。

十、文档字符串

函数写出来是给别人(包括以后忘了的自己)用的。在 def 下面用三引号 ''' 写一段说明,就是文档字符串(docstring)。

Python
def area(r):
    '''计算半径为 r 的圆的面积'''
    return 3.14 * r * r

print(area.__doc__)

输出 计算半径为 r 的圆的面积。

别人想看你怎么用,可以调 help():

Python
help(area)

输出:

Python
Help on function area in module __main__:

area(r)
    计算半径为 r 的圆的面积

help() 显示的,正是你写的文档字符串。所以写函数时顺手写一句它是干啥的,非常值。

文档字符串写在函数体最前面,用三个单引号或三个双引号包起来。它不会参与计算,只是给人看的说明。养成习惯,以后读别人代码会轻松很多。

课程思政|科技自立自强 · 让别人也用得起来

函数写出来是给别人用的,顺手用三引号补一句说明,别人调 help() 一下就明白怎么用。别小看这句话——能把工具写清楚、交得出去,才算真正掌握了它。我们用的很多基础库都来自国外,它们能流行,靠的不只是功能强,还有规范的文档。把命名、注释、说明当成习惯,将来你写的函数,才可能被更多人放心地拿去用。

十一、十个基础案例

下面十个例子都很短,建议每个都亲手敲一遍、跑一遍。它们覆盖函数最常见的用法。

1. 奶茶店点单

Python
def order(drink, size='中杯'):
    return size + drink + ',请慢用'

print(order('珍珠奶茶'))
print(order('美式', '大杯'))

输出:

输出
中杯珍珠奶茶,请慢用
大杯美式,请慢用

2. 快递运费计算

Python
def shipping(weight):
    return 8 + weight * 2

print(shipping(1))
print(shipping(3))

输出:

输出
10
14

3. 外卖满减判断

Python
def can_free(total):
    if total >= 20:
        return True
    return False

print(can_free(25))
print(can_free(12))

输出:

输出
True
False

4. 演唱会验票

Python
def check_ticket(age, has_ticket):
    if age >= 18 and has_ticket:
        return '验票通过,可以入场'
    return '验票不通过,不能入场'

print(check_ticket(20, True))
print(check_ticket(20, False))

输出:

输出
验票通过,可以入场
验票不通过,不能入场

5. 算一份成绩的平均分

Python
def average(scores):
    return sum(scores) / len(scores)

print(average([88, 92, 75, 96]))

输出 87.75。

6. 外卖券后价

Python
def pay(price, coupon):
    if coupon == '满30减10':
        return price - 10
    if coupon == '新客减5':
        return price - 5
    return price

print(pay(32, '满30减10'))
print(pay(18, '新客减5'))
print(pay(18, '无'))

输出:

输出
22
13
18

7. 体测成绩等级

Python
def level(score):
    if score >= 90:
        return '优秀'
    if score >= 80:
        return '良好'
    if score >= 60:
        return '及格'
    return '不及格'

print(level(95))
print(level(73))
print(level(45))

输出:

输出
优秀
及格
不及格

8. 校园卡号校验

Python
def check_card(card):
    if len(card) == 8 and card.isdigit():
        return '卡号有效'
    return '卡号无效'

print(check_card('20261309'))
print(check_card('123'))

输出:

输出
卡号有效
卡号无效

9. 宿舍点名

Python
def count_in(replies):
    n = 0
    for r in replies:
        if r == '到':
            n += 1
    return n

print(count_in(['到', '到', '缺', '到']))

输出 3:4 个床位里有 3 个人答了「到」。

10. 歌词生成器

Python
def make_lyric(singers):
    result = []
    for s in singers:
        result.append(s + '唱一句')
    return result

print(make_lyric(['我', '你', '他']))

输出 ['我唱一句', '你唱一句', '他唱一句']:把每个人接一句,拼成一段词。

十二、常见坑

坑 1:忘写 return,结果拿到 None

Python
def add(a, b):
    a + b          # 算了却没 return

print(add(2, 3))   # 输出 None

输出 None。想拿到和,必须 return a + b。

坑 2:调用时忘加括号

Python
def greet(name):
    return '你好,' + name

print(greet)        # 少了 (),打印的是函数对象

输出类似 <function greet at 0x...>。记住:想运行函数,括号不能省。

坑 3:参数个数对不上

Python
def add(a, b):
    return a + b

print(add(1))       # 少一个,报 TypeError
报错
TypeError: add() missing 1 required positional argument: 'b'

坑 4:默认参数写成可变对象

这是个经典大坑:把默认值设成列表 [],这个列表会在多次调用之间共用,导致数据串味:

Python
def add_item(x, lst=[]):
    lst.append(x)
    return lst

print(add_item(1))
print(add_item(2))

输出:

输出
[1]
[1, 2]

第二次调用本想从空列表开始,结果却带着上一次的 1。正确做法:默认用 None,在函数里新建:

Python
def add_item(x, lst=None):
    if lst is None:
        lst = []
    lst.append(x)
    return lst

print(add_item(1))
print(add_item(2))

输出 [1]、[2],两次互不干扰。

坑 5:函数里改了列表,外面也变了

列表是「引用传递」,函数里改它,外面的原列表跟着变:

Python
def add_score(scores, s):
    scores.append(s)

ns = [90]
add_score(ns, 80)
print(ns)

输出 [90, 80]。这不是 bug,但新手常以为函数「动不到外面的东西」。如果不想影响外面,就传一份副本:add_score(ns.copy(), 80)。

字符串、数字是「改不动外面」的;列表、字典是「会改到外面」的。拿不准时,想想你传进去的东西到底能不能被原地修改。

AI 助手小贴士|函数设计题要先想签名再动手,让 AI 当教练。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 3 道函数设计题:温度转换、判断质数、计算购物小票。要求我先写函数签名和文档字符串再写实现,写完你点评

十三、报错手册

下面这些报错都是真实运行出来的,照抄本机解释器原文。看到眼熟的,直接对照处理。

Python
print(greet('小明'))
def greet(name):
    return '你好,' + name
报错
NameError: name 'greet' is not defined

原因:调用写在定义前面。Python 从上往下读,读到调用时还没见过 greet。

Python
def add(a, b):
    return a + b

print(add(1))
报错
TypeError: add() missing 1 required positional argument: 'b'

原因:位置参数少传了一个。补上第二个值即可。

Python
def add(a, b):
    return a + b

print(add(1, 2, 3))
报错
TypeError: add() takes 2 positional arguments but 3 were given

原因:位置参数多传了。要么少传一个,要么把第三个参数改成关键字参数。

Python
def greet(name='同学', age):
    return name
报错
SyntaxError: parameter without a default follows parameter with a default

原因:有默认值的参数不能排在没有默认值的参数前面。把 age 放到 name 前面。

Python
def greet(name):
    return name

print(greet(n='小明'))
报错
TypeError: greet() got an unexpected keyword argument 'n'

原因:关键字调用的名字必须和定义里的参数名一样。这里没有叫 n 的参数。

Python
def greet(name)
    print(name)
报错
SyntaxError: expected ':'

原因:函数定义那一行结尾漏了冒号。

Python
def 1func():
    print('hi')
报错
SyntaxError: invalid decimal literal

原因:函数名不能以数字开头。改成 func1 之类。

Python
count = 0
def inc():
    count = count + 1
inc()
报错
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

原因:函数里想改全局变量 count 却没写 global count。

Python
def f():
    print('hi')

x = f()
print(x + 10)
报错
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

原因:f() 只 print 没 return,x 是 None,None + 10 报类型错。把 print 改成 return。

Python
def greet(name):
    return '你好,' + name
print(greet)
输出
<function greet at 0x...>

注意:这不是报错,而是「忘了加括号」。打印出来的是函数对象,要加 () 才运行。

十四、函数也是值:赋值、传参、lambda

到了这里,你其实可以把函数当成和普通数字、字符串一样的东西来用:能存进变量、能当参数传来传去。理解这一点,后面学列表、学数据处理会轻松很多。

函数可以赋给变量

函数名本身就是一个「值」,可以把它赋给另一个名字,用新名字也能调用:

Python
def greet(name):
    return '你好,' + name

say_hi = greet        # 把函数本身赋给 say_hi
print(say_hi('小明'))

输出 你好,小明。say_hi 和 greet 指向同一个函数。

函数可以当参数传进去

把函数作为另一个函数的参数,让对方在合适的时机调用它。最常见的是 max / min 配 key=:

Python
drinks = ['珍珠奶茶', '柠檬水', '芋泥啵啵奶茶', '美式']
print(max(drinks, key=len))

输出 芋泥啵啵奶茶(名字最长)。key=len 告诉 max「按名字长度比大小」,而不是按字母顺序。

lambda:一句话写个小函数

有些函数只用一次、逻辑很短,可以用 lambda 当场写,省得专门 def:

Python
menu = [('珍珠奶茶', 15), ('柠檬水', 8), ('美式', 12)]
print(sorted(menu, key=lambda x: x[1]))

输出:

输出
[('柠檬水', 8), ('美式', 12), ('珍珠奶茶', 15)]

lambda x: x[1] 的意思是「取每个元组的第 2 项(价格)来比大小」,它等价于临时写一个 def price(x): return x[1] 的小函数,只是写在了一行里。初学阶段不要求会用 lambda,认识长相即可;正规写函数还是推荐 def,更清楚。

这一节是「进阶视野」:函数能当值传来传去,是 Python 很灵活的地方。大一阶段先把 def 定义、调用、参数、返回值练熟,lambda 和「函数当参数」以后自然就懂了。

一张总览表

概念一句话例子
定义用 def 造一个函数def greet(name):
调用写函数名加括号才运行greet('小明')
参数把外面的值传进去def add(a, b)
返回值用 return 把结果交出来return a + b
复用一次写好,到处调用调用十次只写一遍逻辑

AI 助手小贴士|函数写完别急着交,让 AI 当评审:参数设计、返回值、命名是否达意——评审意见比自己闷头改有价值。

打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:请评审我写的这个函数:参数设计合不合理、该不该有返回值、函数名和注释是否达意?按「问题→建议→改进示例」的格式回答:<粘贴函数>

十五、随堂自测(12 题)

点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。

已答 0 / 12 · 正确 0 —

1想定义一个函数,必须用哪个关键字开头?

  • A. def
  • B. func
  • C. function
  • D. define
答案:A。Python 里定义函数一律用 def,后面跟函数名、括号和冒号。B/C 是别的语言的说法,define 是 def 的英文全称但不是关键字。

2一个函数里只写了计算、没写 return,调用它得到什么?

  • A. 0
  • B. None
  • C. 空字符串
  • D. 会报错
答案:B。没有 return,函数就没有把任何值交回来,调用结果一律是 None。「没写 return」和「报错」要分清。

3print(x) 和 return x 最关键的区别是什么?

  • A. print 把 x 存进变量,return 只是显示
  • B. print 和 return 完全一样
  • C. print 是给人看的显示,return 是把值交给程序继续用
  • D. return 不能用于函数里
答案:C。print 只在屏幕显示给人看,后面没法接着算;return 把值交回调用处,能被变量接住继续用。需要下一步计算一定要用 return。

4关于「先定义后调用」,下面说法正确的是?

  • A. 函数可以写在调用之后,Python 会自动去找
  • B. 调用必须写在 def 定义之后
  • C. 定义和调用谁先谁后都行
  • D. 函数必须在文件最前面定义
答案:B。Python 从上往下执行,读到调用那行时必须已经见过这个函数的定义,否则报 NameError。

5写出 def f(*args): 后,调用 f(1, 2, 3),args 收到的是什么类型?

  • A. 列表 list
  • B. 元组 tuple
  • C. 字典 dict
  • D. 字符串 str
答案:B。一个星号的 *args 把所有位置参数装进一个元组;两个星号的 **kwargs 才装进字典。

6函数定义 def greet(name='同学'):,调用时写 greet() 会发生什么?

  • A. 报错,必须传 name
  • B. name 变成 None
  • C. 用默认值,打印/返回 '你好,同学'
  • D. 打印函数对象
答案:C。有默认值的参数调用时可不传,自动用 '同学'。greet('小明') 才会覆盖成小明。

7要在函数里修改函数外面的全局变量,必须怎么做?

  • A. 直接赋值就行,不用特别写
  • B. 用 global 声明
  • C. 把变量当参数传进去
  • D. 不可能修改
答案:B。函数内想改全局变量,必须先写 global 变量名 声明,否则 Python 会当成一个新的局部变量,报 UnboundLocalError。

8函数写 return 1, 2, 3,调用处用变量接,实际上拿到的是什么?

  • A. 报错
  • B. 三个独立的值
  • C. 一个元组 (1, 2, 3)
  • D. 一个列表 [1, 2, 3]
答案:C。「返回多个值」本质是返回一个元组。用 a, b, c = f() 可以一次拆开接住。

9一个函数里同时有普通参数、默认值参数、*args、**kwargs,正确顺序是?

  • A. 普通 → 默认值 → *args → **kwargs
  • B. 默认值 → 普通 → *args → **kwargs
  • C. **kwargs → *args → 普通 → 默认值
  • D. 随便排都行
答案:A。口诀:普通参数 → 默认值参数 → *args → **kwargs,从左到右。写反会直接 SyntaxError。

10为什么默认参数不要写成 def f(x, lst=[]): 这样的可变对象?

  • A. 列表不能当默认参数,会报错
  • B. 这个默认列表会在多次调用之间共享,数据会串味
  • C. 默认值只能是数字
  • D. 调用时无法传列表进去
答案:B。可变默认对象只创建一次,多次调用会共用同一个列表,第二次调用还带着第一次的数据。正确做法是用 None 当默认、函数内新建。

11关于嵌套函数,下面说法正确的是?

  • A. 内层函数可以直接使用外层函数里定义的变量
  • B. 内层函数必须写 global 才能看到外层变量
  • C. 外层函数不能调用内层函数
  • D. 嵌套函数只能写在类里面
答案:A。内层函数能读到外层函数的变量,不用写关键字;如果要在内层修改它,才需要 nonlocal。内层函数只在外层里可见,出了外层就访问不到。

12下面哪一种描述说的正是「闭包」?

  • A. 把函数赋值给一个变量
  • B. 在函数里调用自己
  • C. 把一个列表当参数传给函数
  • D. 内层函数被返回后,仍然记得外层函数的变量
答案:D。闭包=内层函数 + 它记住的外层变量。外层函数执行完,内层函数仍能用到那些变量(如 make_adder(5) 返回的函数一直记着 n = 5)。在函数里调用自己那是递归。