Python基础学习-07
一、函数定义与参数
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 | 函数默认返回 None | def f(): pass → f() 得到 None |
| 提前返回 | 可用作"守门员"提前拦截无效输入 | if x<0: return |
| return vs print | print 只是显示,不传递数据;return 才把数据交出去给其他代码用 | — |
1.2 LEGB作用域规则
Python 查找变量时遵循 L → E → G → B 四级顺序
| 层级 | 全称 | 范围 | 生命周期 |
|---|---|---|---|
| L | Local | 当前函数内部 | 函数调用时创建,返回时销毁 |
| E | Enclosing | 外层嵌套函数的局部作用域 | 外层函数调用期间 |
| G | Global | 模块(文件)全局 | 模块加载到程序退出 |
| B | Built-in | Python 内置(如 print、len) | Python 启动到退出 |
例子:在嵌套函数内部访问变量 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的元素(""、0、None、[]等)。
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']
说明:
key是sorted()最强大的参数。它告诉 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返回迭代器,在处理超大文件时比列表推导式更省内存(惰性求值)。但日常开发中列表推导式更快且更可读。
更多推荐
所有评论(0)