《Python 函数进阶语法全解析:从参数技巧到函数式编程》
本文系统讲解 Python 函数进阶用法,涵盖参数高级特性(位置 / 关键字参数、默认值、*args/**kwargs)及混合使用规则。深入解析函数注解、闭包、装饰器等高级特性,演示装饰器实现日志、计时等功能的实战案例。介绍生成器函数与 yield 用法,对比生成器与普通函数的内存效率差异。最后讲解函数式编程工具(lambda、map、filter、reduce)及应用场景,帮助开发者掌握函数设计
函数进阶语法
1. 函数参数的高级用法
1.1 位置参数与关键字参数
位置参数是最常见的参数传递方式,根据函数定义时参数的顺序,依次将实参赋值给形参。
- 必须按顺序传递,数量和类型必须与函数定义匹配。
- 调用时不指定参数名,仅通过位置对应。
关键字参数通过参数名(关键字)指定对应关系,无需按顺序传递。
- 调用时使用
参数名=值
的形式。 - 可以跳过某些参数(如果有默认值)或按任意顺序传递。
# 位置参数
def greet(name, message):
return f'{name},{message}!'
# 位置参数 按照参数的顺序进行赋值
print(greet("张三", "早上好"))
print(greet('早上好', '张三'))
# 关键字参数 指定某一个参数为某一个值 顺序可以打乱
print(greet(name = "张三", message="早上好"))
print(greet(message="早上好", name="张三"))
位置参数必须在关键字参数之前
# 如果要和位置参数混用的,位置参数必须在关键字参数之前
# SyntaxError: positional argument follows keyword argument
# print(greet(name="张三", "早上好"))
print(greet("张三", message="早上好"))
仅限位置参数:使用 /
标记之前的参数不能通过关键字传递
# 使用 `/` 标记之前的参数**不能**通过关键字传递
def func(a, b, /, c):
pass
func(1, 2, 3)
func(1, 2, c=3)
# TypeError: func() got some positional-only arguments passed as keyword arguments: 'a, b'
func(a=1, b=2, c=3)
仅限关键字参数:使用 *
标记之后的参数必须通过关键字传递
# 使用 `*` 标记之后的参数必须通过关键字传递
def func(a, b, *, c, d):
pass
# TypeError: func() takes 2 positional arguments but 4 were given
# func(1, 2, 3, 4)
# SyntaxError: positional argument follows keyword argument
# func(a = 1, b = 2, 3, 4)
# func(1, 2, c=3, 4)
func(1, 2, c=3, d=4)
1.2 默认参数值
是函数定义时为参数指定的预定义值。当调用函数时,如果没有为这些参数提供实际值,则会使用默认值。
# 默认参数
def greet(name, message="你好"):
return f'{name},{message}!'
print(greet("王五"))
print(greet("王五", "早上好"))
print(greet("王五", message="晚上好"))
arr = ["123","23","3"]
arr.sort(reverse=True, key=len)
print(arr)
# 第一层含义 给调用者提示
# 第二层含义 增加函数的功能
必须在位置参数之后:默认参数不能出现在位置参数之前,否则会导致语法错误。
# 错误示例
def func(a = 1 ,b):
pass
默认值在函数定义时计算一次:默认值在函数定义时被创建,而不是每次调用时重新创建。这可能导致可变对象(如列表、字典)的意外行为。
def add_item(item, items=[]):
items.append(item)
return items
# 多个add_item的调用使用的都是同一个items
# 在第一次调用时 给items进行初始值
print(add_item(1)) # 1
print(add_item(2)) # 1 2
arr1 = []
print(add_item(3,arr1)) # 3
arr2 = []
print(add_item(4, arr2))# 4
print(add_item(5))
print(add_item(6))
- 修正方法:使用
None
作为默认值,并在函数内部初始化可变对象。
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item(1)) # 1
print(add_item(2)) # 2
arr1 = []
print(add_item(3,arr1)) # 3
arr2 = []
print(add_item(4, arr2))# 4
print(add_item(5)) # 5
print(add_item(6)) # 6
多个默认参数:函数可以有多个默认参数,调用时可选择性地覆盖部分或全部默认值
def calculate(a, b=1, c=2):
return a + b * c
print(calculate(10))
print(calculate(10,3))
print(calculate(10,3,4))
print(calculate(10, c = 4, b = 5))
默认参数与关键字参数结合:默认参数通常与关键字参数一起使用,允许跳过某些参数或按任意顺序传递参数
def config(host="localhost", port=8080, debug=False):
print(f"{host},{port},{debug}")
config(debug=True)
config(port=3000)
config("127.0.0.1",80,False)
仅限关键字参数与默认值:使用 *
分隔符可以定义仅限关键字的默认参数
def func(a , *, b=10):
return a + b
# 错误
# print(func(5,20))
print(func(5,b=20))
1.3 可变参数 *args
允许函数接收任意数量的参数,无需在定义时明确指定参数的个数。
# 可变参数 传入一个列表/元组 接受任意个参数
def sum_all(base, r=1, *numbers):
total = base
for num in numbers:
total = total + num ** r
return total
print(sum_all(10, 2, 1,2,3,4,5,6,7,8))
# 可变参数 传入一个列表/元组 接受任意个参数
def sum_all(base, *numbers, r=1):
total = base
for num in numbers:
total = total + num ** r
return total
print(sum_all(10, 1,2, 3, 4, 5, 6, 7, 8, r=2))
# 可变参数 传入一个列表/元组 接受任意个参数
def sum_all(*numbers, base, r=1):
total = base
for num in numbers:
total = total + num ** r
return total
print(sum_all(1, 2, 3, 4, 5, 6, 7, 8,base = 10,r=2))
# 可变参数 传入一个列表/元组 接受任意个参数
def sum_all(*numbers, r=1, base):
total = base
for num in numbers:
total = total + num ** r
return total
print(sum_all(1, 2, 3, 4, 5, 6, 7, 8, r=2, base=10))
def sum_all(*numbers, r=1, base):
total = base
for num in numbers:
total = total + num ** r
return total
arr = [1,2,3,4,5]
# 使用列表或元素进行解包
print(sum_all(*arr, r = 2, base=10))
1.4 关键字可变参数 **kwargs
允许函数接收任意数量的关键字参数(即带名称的参数),这些参数在函数内部会被收集为一个字典(dict)。
def create_profile(**user_info):
profile = []
for key , value in user_info.items():
profile.append(f'{key}:{value}')
return profile
print(create_profile(name="张三", age=30, city="北京"))
user = {'name':'李四', 'age':30, 'job':'工程师'}
# 将字典数据解包为关键字可变参数
print(create_profile(**user))
与普通参数混合使用:关键字可变参数必须放在函数参数列表的最后,且可与普通参数、位置可变参数组合使用
def func(a, b=10, *args, c=10, **kwargs):
print(f'位置参数a = {a}')
print(f'默认参数b = {b}')
print(f'默认参数c = {c}')
print(f'可变参数args = {args}')
print(f'关键字可变参数kwargs = {kwargs}')
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
func(1)
func(1, 2)
func(1, 2, 3, 4, 5, 6, c=7)
func(1, 2, 3, 4, 5, 6, c=7, x=8, y=9)
func(1,3,3,4,5,6,x=7,y=8,z=9)
1.5 混合使用各种参数
- 参数顺序:位置参数 → 默认参数 → 可变参数(
*args
)→ 关键字参数 → 关键字可变参数(**kwargs
)。 - 调用方式:
- 位置参数按顺序传递,无需指定名称。
- 默认参数可省略,或通过关键字覆盖。
- 可变参数接收额外的位置参数,打包为元组。
- 关键字参数通过
name=value
传递。 - 关键字可变参数接收额外的关键字参数,打包为字典。
2. 函数的高级特性
2.1 函数注解
是一种为函数参数和返回值添加元数据的语法。它允许你为参数和返回值关联任意类型的表达式(通常是类型),但不会影响函数的实际行为。函数注解主要用于文档、类型检查或第三方工具(如静态类型检查器、IDE 提示)。
- 函数注解使用冒号(
:
)为参数添加类型提示,使用->
为返回值添加类型提示:
def add(a:int,b:int) -> int:
return a + b
print(add("123","456"))
- 注解的特点:
- 不强制类型检查:Python 解释器不会强制执行注解的类型,它们只是元数据。
- 可存储任意表达式:注解可以是任何类型的对象(如字符串、类、函数等),但通常用于类型提示。
- 通过
__annotations__
属性访问:函数的注解存储在其__annotations__
属性中。
def add(a:"这是参数a建议整数",b:"这是参数b建议整数") -> "返回值建议整数":
return a + b
print(add("123","456"))
print(add.__annotations__)
2.2 闭包函数
是指有权访问另一个函数作用域中的变量的函数。即使该外部函数已经执行完毕,其作用域内的变量也不会被销毁,而是会被闭包捕获并保留在内存中。闭包使得函数可以 “记住” 它创建时的环境,即使环境已经不存在。
闭包形成的三个条件:
- 函数嵌套:内部函数定义在外部函数内部。
- 内部函数引用外部函数的变量:内部函数使用了外部函数作用域中的变量。
- 外部函数返回内部函数:外部函数将内部函数作为返回值。
def outer_function(x):
def inner_function(y):
return x + y # 内部函数引用外部函数的变量x
return inner_function
func = outer_function(10)
print(func(5))
闭包的特点:
- 捕获变量值:闭包捕获的是变量的值,而不是变量本身。
- 保持状态:闭包可以记住其创建时的环境,即使外部函数已经执行完毕。
- 独立实例:每次调用外部函数都会创建一个新的闭包实例,它们之间互不影响。
def outer_function(x):
def inner_function(y):
return x + y # 内部函数引用外部函数的变量x
return inner_function
func = outer_function(10)
print(func(5))
print(func(10))
func = outer_function(20)
print(func(10))
print(func(20))
# 做一个计数器
def counter():
count = 0;
def increment():
# 如果需要再闭包中修改外部函数的变量,必须使用nonlocal声明
nonlocal count
count += 1
return count
return increment
c1 = counter() # 创建一个计数器
print(c1()) # 计数+1
print(c1()) # 计数+1
print(c1()) # 计数+1
c2 = counter()# 创建另一个计数器
print(c2())
闭包与普通函数区别
特性 | 闭包 | 普通函数 |
---|---|---|
捕获外部变量 | 可以访问并保留外部函数的变量 | 只能访问全局变量或参数(局部变量) |
状态保持 | 每个闭包实例独立维护状态 | 不保存状态,每次调用独立执行 |
创建方式 | 通过函数嵌套并返回内部函数 | 直接定义 |
2.3 装饰器
是一种特殊的函数或类,用于在不修改原函数代码的情况下,扩展或修改函数的行为。装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。
装饰器的核心作用是增强函数功能,例如添加日志、计时、权限验证等功能,而不需要修改原函数的代码。
使用 @装饰器名
语法糖将装饰器应用到函数上
基本装饰器
计时装饰器
import time
# def selection_sort(arr):
# for i in range(0, len(arr) - 1):
# for j in range(i + 1, len(arr)):
# if arr[i] > arr[j]:
# arr[i], arr[j] = arr[j], arr[i]
# return arr
#
# def bubble_sort(arr):
# for i in range(0, len(arr) - 1):
# for j in range(0, len(arr) - 1 - i):
# if arr[j] > arr[j + 1]:
# arr[j], arr[j + 1] = arr[j + 1], arr[j]
# return arr
#
# def sort_timer(sort_func, arr):
# start = time.time()
# sort_func(arr)
# end = time.time()
# print(end - start)
#
# arr = [1,5,2,6,2,7,9,3,5,7,3,2]
# # 排序 和 计时 的一个组合而已
# sort_timer(selection_sort, arr)
# sort_timer(bubble_sort, arr)
# selection_sort(arr)
# 计时装饰器函数
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间:{end - start:0.6f}秒数")
return result
return wrapper
@timer
def selection_sort(arr):
for i in range(0, len(arr) - 1):
for j in range(i + 1, len(arr)):
if arr[i] > arr[j]:
arr[i], arr[j] = arr[j], arr[i]
return arr
def bubble_sort(arr):
for i in range(0, len(arr) - 1):
for j in range(0, len(arr) - 1 - i):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
arr = [1,5,2,6,2,7,9,3,5,7,3,2]
print(selection_sort(arr))
print(timer(bubble_sort)(arr))
日志装饰器:
def log_decorator(func):
def wrapper(*args, **kwargs):
print(f'调用函数{func.__name__},参数:{args}, {kwargs}')
result = func(*args, **kwargs)
print(f'计算结果:{result}')
return wrapper
@log_decorator
def add(a, b):
return a + b
@log_decorator
def subtract(a, b):
return a - b
@log_decorator
def multiply(a, b):
return a * b
@log_decorator
def divide(a, b):
return a / b
add(1,3)
subtract(1,3)
multiply(1,3)
divide(1,3)
多个装饰器
def bold(func):
def wrapper(text):
print("bold")
print(func.__name__)
return f'<b>{func(text)}</b>'
return wrapper
def italic(func):
def wrapper(text):
print("italic")
print(func.__name__)
return f'<i>{func(text)}</i>'
return wrapper
# 多个装饰器应用顺序从下到上的
@italic # 装饰器的装饰器
@bold
def format_text(text):
return text
print(format_text("Hello World"))
"""
<b><i>文本数据</i></b>
"""
2.4 函数作为对象
Python当中,万事万物皆对象,函数也可以当做参数或者返回值进行传递!
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def multiply(a, b):
return a * b
def divide(a, b):
return a / b
operations = {
"+": add,
"-": subtract,
"*": multiply,
"/": divide
}
# 第一种调用方式
def calculate(operation, a, b):
return operation(a, b)
print(calculate(add, 10, 5))
# 第二种调用方式
op = "*"
print(operations[op](10, 5))
# 第三种调用方式
def choice_operation(op):
operation = operations[op]
return operation # add
print(choice_operation("+")(10,5))
print(choice_operation("-")(10,5))
2.5 函数组合
"""
f(x) g(x) h(x)
f(g(h(x)))
"""
def compose(*functions):
def inner(x):
result = x
for f in reversed(functions):
result = f(result)
return result
return inner
def add(x):
return x + 1
def double(x):
return x * 2
def square(x):
return x ** 2
f = compose(square, double, add)
print(f(5)) # ((5+1)*2)^2
3. 生成器函数
是一种特殊的函数,它使用 yield
关键字代替 return
来返回值,并且可以在每次调用时暂停和恢复执行状态。生成器函数创建的对象称为生成器(Generator),它是一种迭代器,支持惰性计算,能显著节省内存。
yield
关键字:生成器函数使用yield
暂停执行并返回一个值,下次调用时从暂停处继续执行。- 惰性计算:生成器只在需要时生成值,适合处理大量数据或无限序列。
- 状态保存:生成器会自动保存函数的局部状态,无需额外管理(利用了闭包的特性)。
3.1 基本生成器
生成数字
# 生成从1到n的数字
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
# 生成器函数(迭代器)
counter = count_up_to(3)
print(next(counter))
print(next(counter))
print(next(counter))
# StopIteration
# print(next(counter))
for num in count_up_to(3):
print(num)
无限斐波那契数列
"""
1 1 2 3
a b
1 1 2
"""
def fibonacci():
a, b = 1, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
n = 0
while n < 1000:
print(next(fib))
n += 1
3.2 生成器表达式
生成器只能遍历一次,遍历结束后需要重新创建。
# 列表推导式 推出所有的元素
arr = [x ** 2 for x in range(1,6)]
print(arr)
# 数据已有 挨个遍历
for num in arr:
print(num)
# 生成器表达式 不会推出所有的元素
arr = (x ** 2 for x in range(1,6))
print(arr)
# 数据没有 挨个生成
for num in arr:
print(num)
# 此时元素已经生成完毕 生成结束
for num in arr:
print(num)
3.3 yield from 语句
yield from
是一个用于简化生成器嵌套的语法,它允许一个生成器委托部分操作给另一个生成器。
就是将多个数据源创(序列,集合,生成器函数,迭代器)建为一个生成器
def data1():
yield from [1, 2, 3]
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
def data2():
yield from count_up_to(5)
yield from data1()
yield from ('a', 'b', 'c')
yield from range(4, 6)
for item in data2():
print(item)
生成器 vs. 普通函数
特性 | 生成器函数 | 普通函数 |
---|---|---|
返回关键字 | 使用 yield 多次返回值 |
使用 return 一次性返回值 |
执行状态 | 暂停和恢复,保留局部状态 | 执行完毕后销毁所有状态 |
内存效率 | 按需生成,节省内存 | 一次性生成所有结果,可能占用大量内存 |
返回类型 | 生成器对象(迭代器) | 返回具体值 |
可迭代次数 | 一次性迭代,耗尽后需重建 | 可重复调用 |
4. 函数式编程
4.1 lambda 表达式
是一种用于创建匿名函数的简洁语法。它允许在需要函数的地方直接定义轻量级函数,无需使用 def
关键字显式命名。lambda 表达式通常用于简单的函数逻辑,例如在高阶函数(如 map
、filter
、sorted
)中作为参数传递。
基本语法
lambda 参数列表: 表达式
- 参数列表:可以是单个参数、多个参数,甚至可变参数(如
*args
、**kwargs
)。 - 表达式:函数的返回值,只能是一个表达式(不能包含语句,如
if
、for
等)。
greet = lambda: "Hello!"
print(greet())
"""
def greet():
return "Hello"
"""
square = lambda x: x ** 2
print(square(5))
"""
def square(x):
return x ** 2
"""
add = lambda x, y:x + y
print(add(1,2))
"""
def add(x, y):
return x + y
"""
与普通函数的区别
特性 | lambda 表达式 | 普通函数(def) |
---|---|---|
定义方式 | 无名称,使用 lambda 关键字 |
有名称,使用 def 关键字 |
表达式数量 | 只能有一个表达式(返回值) | 可以包含多个语句(如 if 、循环) |
文档字符串 | 不支持 | 支持(__doc__ 属性) |
调试难度 | 较难(无名称,堆栈信息不明确) | 较容易(有名称,堆栈信息清晰) |
适用场景 | 简单逻辑、临时使用 | 复杂逻辑、需要重复调用 |
students = [
{"name": "张三", "score": 85},
{"name": "李四", "score": 92},
{"name": "王五", "score": 78},
]
s = sorted(students, key=lambda item:item['score'], reverse=True)
print(s)
def my_key(item):
return item['score']
s = sorted(students, key=my_key, reverse=True)
print(s)
4.2 map 函数
# 使用map函数将传入的函数应用到可迭代对象的每个元素
numbers = [1, 2, 3, 4, 5]
# l = [x ** 2 for x in numbers]
# l = (x ** 2 for x in numbers)
l = list(map(lambda x: x ** 2, numbers))
print(l)
def cube(x):
return x ** 3
l = list(map(cube, numbers))
print(l)
l1 = [1, 2, 3]
l2 = [10, 20, 30]
l3 = list(map(lambda x, y: x + y, l1, l2))
print(l3)
4.3 filter 函数
# 使用filter 函数筛选可迭代对象中的元素
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = list(filter(lambda x: x % 2 == 0, numbers))
print(l)
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
l = list(filter(is_prime, numbers))
print(l)
l = list(filter(lambda x:is_prime(x), numbers))
print(l)
4.4 reduce 函数
# 使用reduce函数对迭代对象中的元素进行累积操作
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# sum(numbers)
l = reduce(lambda x, y: x + y, numbers)
print(l)
# 阶乘/累成
l = reduce(lambda x, y: x * y, numbers)
print(l) # 1 * 2 * 3 * 4 * 5
# 错误示例 返回1 耗时操作
l = reduce(lambda x, y: x ** y, numbers)
print(l)
print(1 ** 2 ** 3 ** 4 ** 5)
更多推荐
所有评论(0)