前言
函数可以把一段逻辑封装起来,调用时传入参数,执行后再通过返回值交回结果。理解函数的定义、参数和返回值后,再看不定长参数、函数作为参数以及订单金额计算,就能看清函数如何处理和传递数据。
一、定义函数并调用
使用 def 定义函数,函数名后面的小括号用于声明参数,缩进代码是函数体。定义函数时,函数体不会立即执行;只有调用函数时,里面的代码才会运行。
python
def one():
print("hello")
print("a")
one()
程序先输出 a,再调用 one(),输出 hello。调用时写函数名和括号,即使函数不需要参数,也不能省略括号。
函数可以接收参数,让同一段逻辑处理不同的数据。下面的函数接收半径 r,计算圆的面积并返回结果:
python
def circle(r):
area = 3.14 * r ** 2
return area
a = circle(10)
print(a)
调用 circle(10) 时,参数 10 传给 r,函数计算出面积后通过 return 返回;调用结果被赋给变量 a。按这段代码的计算,输出为 314.0。
二、函数参数:把数据传进去
调用函数时,参数可以按位置传入,也可以通过参数名传入:
python
circle(10) # 位置传参
circle(r=10) # 关键字传参
位置传参按照函数定义中的参数顺序匹配;关键字传参直接指定参数名,可读性更清楚。传入的参数需要和函数定义相匹配。
当需要接收的参数数量不固定时,可以使用 *args 和 **kwargs。*args 会把多余的位置参数收集为元组,**kwargs 会把关键字参数收集为字典:
python
def calc_data(*args, **kwargs):
min_data = min(args)
max_data = max(args)
avg_data = sum(args) / len(args)
if kwargs.get("round") is not None:
avg_data = round(avg_data, kwargs.get("round"))
if kwargs.get("print"):
print(min_data, max_data, avg_data)
return min_data, max_data, avg_data
data = calc_data(100, 200, 300, 400, round=2, print=True)
print(data)
调用时,100、200、300、400 被收集到 args 中;round=2 和 print=True 被收集到 kwargs 中。函数用 args 计算最小值、最大值和平均值,再根据可选配置决定是否保留小数位、是否打印结果。
这里要注意,args 为空时,min(args) 和 max(args) 无法计算,因此这个函数要求调用方至少传入一个数值。
*args 和 **kwargs 也适合区分必需数据与可选配置。奶茶示例中,奶茶名称是核心数据,甜度、冰度和加料是定制选项:
python
def order_milk_tea(*args, **kwargs):
# 核心数据:奶茶名称(必点,数量不确定,可以点多杯)
print(f"点的奶茶:{args}")
# 选项:甜度、冰度、加料、大小等配置参数
if kwargs:
print(f"定制选项:")
for key, value in kwargs.items():
print(f" {key}:{value}")
else:
print(" 无定制选项,按默认制作 ~")
print("-" * 30)
# 场景1:只要一杯奶茶,不定制选项
order_milk_tea("珍珠奶茶")
# 场景2:点奶茶 + 定制各种选项
order_milk_tea("珍珠奶茶", 甜度="少糖", 冰度="去冰", 加料=["布丁", "珍珠"], 大小="大杯")
# 场景3:多杯奶茶 + 部分选项
order_milk_tea("珍珠奶茶", "烤奶", "杨枝甘露", 甜度="无糖", 大小="中杯")
第一种调用只传入奶茶名称,kwargs 为空;后两种调用还传入了定制选项。这个写法适用于核心数据数量不固定、同时允许调用方指定额外配置的场景。
三、函数返回值:把结果传出来
return 把函数计算出的结果交回调用处。函数可以返回一个值,也可以在 return 后用逗号分隔多个值:
python
def cirle2(r):
"""
返回多个值
:param r:
:return:
"""
return round(3.14 * r ** 2, 1), round(2 * 3.14 * r, 1)
area, circumference = cirle2(10)
print(area)
print(circumference)
这里返回的是圆的面积和周长。Python 会把多个返回值组合成一个元组,因此也可以先用一个变量接收,再查看它的类型:
python
result = cirle2(10)
print(result)
print(type(result))
如果返回结果确定有多个,也可以像前面的示例一样用多个变量解包。使用有含义的变量名,例如 circumference,比使用 len 这类内置函数名更清楚,也能避免覆盖内置名称。
如果函数执行到末尾都没有遇到 return,调用结果会是 None。
四、函数中的变量与 global
函数内部赋值的变量通常属于函数内部。要在函数中给全局变量重新赋值,需要用 global 声明:
python
num = 100
def circle(a):
global num
num = 88
return a * num
b = circle(5)
print(num)
调用 circle(5) 后,num 被改成 88,因此最后输出 88。global num 让函数中的赋值作用于全局变量。由于这种修改会影响函数外部的状态,读代码时需要留意函数是否改变了全局变量。
五、把函数作为参数传递
函数名可以作为参数传给另一个函数。接收函数的代码可以调用它,从而把具体操作交给调用方决定:
python
def add(a, b):
return a + b
def sub(a, b):
return a - b
def mul(a, b, oper):
return oper(a, b)
result = mul(10, 20, add)
print(result)
调用时把 add 传给参数 oper,mul 内部执行 oper(a, b),所以结果为 30。这个例子中,mul 的实际行为由传入的函数决定;传入 sub 时就会执行减法。
简单的函数也可以用 lambda 表达式定义。冒号左侧是参数,右侧是一个表达式:
python
outline = lambda: print("------------")
outline()
add = lambda a, b: a + b
print(add(1, 2))
lambda 适合表达简单、单行的操作。例如,按字符串长度对列表进行降序排序:
python
data_list = ["C++", "C", "Python", "PHP", "Java", "Go"]
data_list.sort(key=lambda item: len(item), reverse=True)
print(data_list)
key 接收用于比较的规则,这里针对每个字符串计算长度;reverse=True 表示按长度从大到小排序。sort() 会直接修改原列表。
六、综合示例:计算订单金额
订单函数通过 *args 接收多件商品,通过关键字参数接收优惠券、积分和运费。每件商品使用一个元组表示,依次包含商品名、单价和数量:
python
def cal_order_cost(*args, coupon, score, express):
"""
根据传入的一批商品信息(商品名,价格,数量),优惠(优惠券,积分抵扣),运费信息计算订单的总金额
:param args: 商品信息(商品名,价格,数量) --->如:("鼠标",188,2)
:param coupon:
:param score:
:param express:
:return:
"""
#1.计算商品总金额
total_price = [goods[1] * goods[2] for goods in args]
total_cost = sum(total_price)
#2.扣减优惠券
if total_cost >= 5000 and coupon <= total_cost:
total_cost -= coupon
if total_cost >= 5000 and score // 100 <= total_cost:
total_cost -= score // 100
# 4.添加运费
total_cost += express
return total_cost
total = cal_order_cost(
("鼠标", 188, 2),
("键盘", 388, 1),
("手机", 3999, 1),
coupon=10,
score=4000,
express=9.9
)
print(total)
由于 coupon、score 和 express 声明在 *args 后面,它们需要通过关键字传入。函数用商品元组的第二项乘以第三项得到每件商品的小计,再将小计求和。
示例中的商品总额是 188 × 2 + 388 × 1 + 3999 × 1 = 4763,低于 5000,因此优惠券和积分抵扣条件都不成立;加上 9.9 运费后,结果为 4772.9。运行脚本得到的输出也是 4772.9。
需要留意积分门槛的判断顺序:代码先扣除优惠券,再检查 total_cost >= 5000。如果商品原价满 5000,但优惠券扣减后低于 5000,当前代码就不会抵扣积分;这与注释中"商品总金额满 5000"的描述不完全一致。score // 100 表示积分按整百换算,例如 4050 积分按 40 元计算。
七、类型注解
类型注解可以标明变量、参数和返回值预期使用的类型,便于读代码和使用类型检查工具:
python
a2: int = 444
names: list[str | int] = ["A", "B", 1, 2]
liststr \| int 表示列表元素可以是字符串或整数。这种注解表达预期类型,但不会自动转换值,也不会在所有情况下阻止类型不匹配;str | int 这种联合类型写法需要 Python 3.10 或更高版本。
函数的类型注解也可以标在参数和返回值上,例如将商品信息标为包含名称、价格和数量的元组,并在函数签名中标明其他参数与返回值的类型。类型注解用于说明约定,不会代替运行时校验。
函数的核心是明确输入和输出:参数说明数据从哪里来,函数体负责处理,返回值把结果交回调用处。设计函数时,先区分必需数据与可选配置,再检查返回值是否能准确表达处理结果。