一、函数定义与参数

1、知识点

1.1 什么是函数?

函数就像一台“料理机”:把原料(参数)放进去,按固定流程加工,最后端出成品(返回值)。用 def 关键字定义,把重复逻辑封装起来,一次编写,随处调用。

def greet(name):
    """向指定的人打招呼"""
    return f"你好,{name}!"

1.2 参数传递方式对比

参数类型语法调用示例核心规则
位置参数def f(a,b)f(3,4)按顺序一一对应,不能多也不能少
默认参数def f(a,b=10)f(3)或f(3,7)有默认值的放后面,调用时可省略
关键字参数def f(a,b)f(b=5,a=3)指定参数名传值,顺序任意
可变位置参数def f(*args)f(1,2,3)接收任意多个位置参数,打包成元组
可变关键字参数def f(**kwargs)f(x=1,y=2)接收任意多个关键字参数,打包成字典

1.3 形参 vs 实参

概念英文定义类比
形参parameter定义函数时括号里的变量名租房合同上的"承租人"空位
实参argument调用函数时实际传入的值签合同时填写的具体姓名

1.4 默认参数的重大陷阱

def add_item(item,target=[]):
    target.append(item)
    return target

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2]  ← 意外!默认值在定义时只计算一次

正确做法:用 None 作占位符,函数体内再初始化。

def add_item(item, target=None):
    if target is None:
        target = []
    target.append(item)
    return target

1.5 docstring (文档字符串)

写在函数体第一行的三引号字符串,描述函数用途。通过 help(函数名)函数名.__doc__ 查看。

2、代码示例

# ============================================================
# 示例1:基础函数——位置参数与默认参数
# ============================================================
def bmi(weight_kg,height_m=1.75):
    """计算BMI指数。weight_kg: 体重(kg), height_m: 身高(m),默认1.75"""
    bmi = weight_kg / (height_m**2)
    return round(bmi,1)

print(bmi(70))           # 使用默认身高 → 22.9
print(bmi(70, 1.80))     # 覆盖默认身高 → 21.6

# ============================================================
# 示例2:关键字参数——顺序随意
# ============================================================

def order_info(product,quantity,price):
    return f"{product} × {quantity},合计 {quantity * price} 元"

print(order_info(product="Python书", price=90, quantity=2))
# Python书 × 2,合计 158 元  ← 参数顺序和定义时不同,靠名字匹配

# ============================================================
# 示例3:*args——接收任意数量位置参数
# ============================================================
def my_sum(*numbers):
    """对任意多个数字求和"""
    return sum(numbers)

print(my_sum(1, 2, 3, 4, 5))   # 15
print(my_sum(10, 20))          # 30

# ============================================================
# 示例4:**kwargs——接收任意数量关键字参数
# ============================================================

def build_profile(name,**info):
    """构建个人信息字典"""
    profile={"name":name}
    profile.update(info)
    return profile

me = build_profile("小明",age=25,city="上海",hobby="编程")
print(me)
# {'name': '小明', 'age': 25, 'city': '北京', 'hobby': '编程'}

# ============================================================
# 示例5:混合使用——完整参数顺序
# ============================================================

def full_demo(name,*score,grade="A",**extra):
    """演示全套参数"""
    print(f"姓名{name}")
    print(f"成绩:{score}")
    print(f"等级{grade}")
    print(f"docstring: {full_demo.__doc__}")


full_demo("小红", 95, 88, 92, grade="A+", club="篮球队", city="上海")

# ============================================================
# 示例6:解包传参——* 和 ** 反过来用
# ============================================================
def add(a, b, c):
    return a + b + c

nums = [1, 2, 3]                # 列表/元组 → 用 * 拆开
print(add(*nums))               # 等价于 add(1, 2, 3) → 6

info = {"a": 10, "b": 20, "c": 30}  # 字典 → 用 ** 拆开
print(add(**info))              # 等价于 add(a=10, b=20, c=30) → 60

二、函数返回值与作用域

1、知识点

函数可以看作是"加工厂"——送入原料,产出成品。今天深入探讨两个关键问题:产出了什么(返回值)和谁有权使用原料(作用域)。

1.1 return:函数的“交货单”

return是函数向外界交付结果的唯一通道,一旦执行return,函数立即结束——后面再多代码也不会执行。

特性说明示例
返回单个值最常见的用法return 42
返回多个值实质上返回一个元组,可用解包接收return a, b → 等价 return (a, b)
无 return函数默认返回 Nonedef f(): passf() 得到 None
提前返回可用作"守门员"提前拦截无效输入if x<0: return
return vs printprint 只是显示,不传递数据;return 才把数据交出去给其他代码用

1.2 LEGB作用域规则

Python 查找变量时遵循 L → E → G → B 四级顺序

层级全称范围生命周期
LLocal当前函数内部函数调用时创建,返回时销毁
EEnclosing外层嵌套函数的局部作用域外层函数调用期间
GGlobal模块(文件)全局模块加载到程序退出
BBuilt-inPython 内置(如 printlenPython 启动到退出

例子:在嵌套函数内部访问变量 x,Python 先看当前函数有没有定义 x(Local),没有则看外层函数(Enclosing),再没有看模块全局(Global),最后查内置(Built-in)。

1.3 global与nonlocal

关键词作用使用场景
global在函数内部声明要修改的是模块级全局变量计数器、配置开关等需要跨函数共享状态时
nonlocal在嵌套函数内部修改外层函数的局部变量闭包中累加状态

1.4 闭包入门

闭包 = 函数+它捕获的外层变量。外层函数返回内层函数时,内层函数“记住”了创建时所在的函数变量,即使外层函数已经执行完毕。
类比:你离家上大学时带着一张银行卡。虽然你离开了家(外层函数结束),但每次刷卡时还是从家里的账户扣钱(访问 Enclosing 变量)。这张卡就是闭包。

2、代码示例

示例1:return 多值返回与解包

def divide(a,b):
    """返回商和余数"""
    if b==0:
        return None# 守门员:提前拦截
    quotient = a // b
    remainder = a % b
    return quotient, remainder  # 返回元组

#两种接收方式
result=divide(10,3)
print(result)    # result = (3, 1)  一个元组
q,r=divide(10,3)   # q=3, r=1  直接解包
print(f"商={q},余数={r}")

示例2:LEGB 查找顺序演示

x="全局"   # global
def outer():
    x="外层"
    def inner():
        x="内部"
        print(f"inner 看到的 x: {x}")
    inner()
    print(f"outer 看到的 x: {x}")

outer()
print(f"全局看到的x:{x}")

示例3:global 修改全局变量

click_count = 0 #全局变量
def record_click():
    global click_count
    click_count +=1

def get_clicks():
    return click_count

record_click()
record_click()
record_click()
print(f"点击次数: {get_clicks()}")  # 3

示例4:nonlocal 与闭包实现计数器

def make_counter(start=0):
    count = start    # Enclosing 变量
    def counter():
        nonlocal count #声名要修改外层的cout
        count += 1
        return count
    return counter   # 返回内层函数,形成闭包

c1 = make_counter(10)    # 从10开始计数
c2 = make_counter(0)     # 另一个独立计数器

print(c1())  # 11
print(c1())  # 12
print(c2())  # 1
print(c1())  # 13  ← c1 和 c2 互不影响

三、Lambda与高阶函数

1、知识点

1.1 核心知识点

Lambda 表达式:一种"一句话函数",不需要 def 定义,用完即扔。像便利贴,临时写个提醒,不用装裱起来。

#传统函数
def add(x,y):
    return x+y

#Lambda 函数
add=lambda x,y:x+y

语法格式:lambda 参数: 返回值表达式

高阶函数:把函数当参数传入、或把函数作为返回值的函数。就像餐厅经理——自己不炒菜,但能调度厨师(函数)去干活。

函数式编程思想:用"数据流过一个个函数"的方式来思考,而不是"一步一步改数据"。想象流水线:原料经过切割→清洗→包装,每一步都是一个纯函数。

1.2 核心对比表

特性普通函数 (def)Lambda 表达式列表推导式
定义方式def name(): ...lambda x: expr[x for x in seq]
行数多行只能一行一行或多行
可读性最好(复杂逻辑)简单逻辑尚可过滤转换极佳
可否含语句可以(if/for等)不能,只能表达式只能表达式
复用性高(有名字可调用)低(通常临时用)低(原地生成)
适用场景复杂逻辑、需文档简单回调、key参数列表生成、过滤
高阶函数作用返回值等价推导式
map(f, seq)对每个元素执行 f迭代器[f(x) for x in seq]
filter(f, seq)保留 f 返回 True 的迭代器[x for x in seq if f(x)]
reduce(f, seq)累积计算(左折叠)单个值无直接等价,需循环
sorted(seq, key=f)按 f 的返回值排序新列表无直接等价

2、代码示例

2.1 Lambda 基础写法

#示例1:最简单的lambda 加法
add = lambda x,y:x+y
print(add(3,5))

#示例2:单参数 平方
square = lambda x:x**2
print(square(3))

#示例3:无参数
greet = lambda:"Hello python"
print(greet())

#示例4:多参数计算平均值
avg = lambda a,b,c:(a+b+c) /3
print(avg(80,90,100))

说明:lambda 冒号左边是参数(可以有多个,用逗号分隔),右边是返回值表达式。不能写 return,表达式结果自动返回。

2.2 map() --批量加工

#场景:一组温度数据(摄氏度),全部转化为华氏度
celsius=[0,15,28,35,100]

#方法A:用map+lambda
fahrenheit = list(map(lambda c: c * 9/5 + 32, celsius))
print(fahrenheit)  # [32.0, 59.0, 82.4, 95.0, 212.0]

#方法B:列表推导式
fahrenheit2=[c*9/5+32 for c in celsius]
print(fahrenheit2)

#示例:字符串列表全部转换大写
names = ['alice','bob','bat']
upper_name = list(map(str.upper,names))
print(upper_name)

说明map() 对序列中每个元素执行同一个函数。注意它返回的是迭代器,需要 list() 才能看到结果。能传已有函数时直接传,不用多包一层 lambda。

2.3 filter()–筛选过滤

#场景:从成绩列表中筛选及格(≥60)的成绩
scores = [45,90,100,33,65,24,99]

#方法A: filter + lambda

passed = list(filter(lambda s:s >= 60,scores))
print(passed)

#方法B:列表推导式

passed2 = [s for s in scores if s >= 60]
print(passed2)

#示例:筛选出非空字符串
items = ["pass","A","!","","bro"]
non_empty=list(filter(None,items))
print(non_empty)

说明**:filter() 保留使函数返回 True 的元素。filter(None, seq) 是快捷写法,会去掉所有布尔值为 False 的元素(""0None[] 等)。

2.4 reduce()–累积规约

from functools import reduce

#场景1:计算阶乘 5!=5*4*3*2*1

factorial = reduce(lambda x,y:x*y,range(1,6))
print(factorial)

#场景2 : 拼接字符串
words = ["python","是","最棒的","语言"]
sentence = reduce(lambda a,b:a+" "+b,words)
print(sentence)

#场景3 带初始值的 reduce (c初始值作为第一个”累计值“)
numbers=[1,2,3,4,5]
total = reduce(lambda  acc,n:acc+n,numbers,100) #从100开始累加

print(total)

说明reduce() 像一个滚雪球的过程——每一步把当前累积值和下一个元素一起传给函数,得到新的累积值。需要从 functools 导入。如果提供初始值,它会在序列之前参与计算。

2.5 sorted() 的 key 参数

#场景:按不同标准顺序

students = [
    {"name": "张三", "score": 85, "age": 20},
    {"name": "李四", "score": 92, "age": 19},
    {"name": "王五", "score": 78, "age": 21},
    {"name": "赵六", "score": 92, "age": 18},
    ]
#按成绩排序(升序)
by_score = sorted(students,key=lambda s:s["score"])
print([s["name"] for s in by_score])

# 按成绩降序,成绩相同按年龄升序(元组多级排序)
by_score_age = sorted(students,key=lambda s:(-s["score"],s["age"]))
print([(s["name"], s["score"], s["age"]) for s in by_score_age])

#示例:按字符串长度排序
words = ["apple", "kiwi", "banana", "pear", "grapefruit"]
by_length = sorted(words,key=len)
print(by_length)
# ['kiwi', 'pear', 'apple', 'banana', 'grapefruit']

# 示例:忽略大小写排序
names = ["Bob", "alice", "Charlie", "david"]
print(sorted(names, key=str.lower))  # ['alice', 'Bob', 'Charlie', 'david']

说明keysorted() 最强大的参数。它告诉 Python “按什么标准比大小”,但不改变元素本身。多级排序的技巧是用元组 (-s["score"], s["age"])——负号实现降序。

3. 补充提示

  • operator 模块operator.itemgetter 可以替代 lambda 做排序 key,速度更快。例如 sorted(students, key=operator.itemgetter("score"))

  • partial 偏函数from functools import partial 可以"冻结"函数的部分参数,创建新函数。例如 mul_by_2 = partial(lambda x, y: x * y, 2) 等同于 lambda y: 2 * y

  • 可调用对象:任何实现了 __call__ 方法的类实例都可以像函数一样调用,比 lambda 更灵活——能保存状态。

  • 性能提示map/filter 返回迭代器,在处理超大文件时比列表推导式更省内存(惰性求值)。但日常开发中列表推导式更快且更可读。

更多推荐