Python 的成功很大程度上归功于其清晰、简洁且功能强大的语法,而函数在其中扮演了核心角色。从简单的代码封装到复杂的函数式编程范式,理解函数的方方面面是成为一名优秀 Python 开发者的必经之路。本文将深入探讨 Python 函数的几个关键高级特性。
1. 灵活多变的函数参数
Python 的函数参数处理非常灵活,主要支持以下四种类型:
1.1 位置参数
这是最常用的参数类型。调用函数时,实参的顺序和数量必须与形参的定义完全匹配。
def greet(name, greeting):
print(f"{greeting}, {name}!")
greet("Alice", "Hello") # 输出: Hello, Alice!
# greet("Hello") # 错误:缺少一个必需的位置参数
1.2 默认参数
在定义函数时,可以为参数指定一个默认值。调用时如果未传递该参数,则使用默认值。默认参数必须指向不可变对象,否则可能引发意想不到的行为。
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Bob") # 输出: Hello, Bob!
greet("Charlie", "Hi") # 输出: Hi, Charlie!
1.3 可变参数 (*args
)
当你不确定要传递多少个位置参数时,可以使用 *args
。它会将传入的所有额外位置参数收集到一个元组中。
def add_numbers(*args):
total = 0
for num in args:
total += num
return total
result = add_numbers(1, 2, 3, 4, 5)
print(result) # 输出: 15
1.4 关键字参数 (**kwargs
)
用于处理不定数量的关键字参数(如 key=value
形式)。它会将这些参数收集到一个字典中。
def print_user_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_user_info(name="Dave", age=30, city="New York")
# 输出:
# name: Dave
# age: 30
# city: New York
混合使用顺序:在定义一个函数时,这四种参数必须遵循严格的顺序:
位置参数 -> 默认参数 -> *args -> **kwargs
def complex_function(a, b=2, *args, **kwargs):
print(f"a={a}, b={b}")
print(f"args: {args}")
print(f"kwargs: {kwargs}")
complex_function(1, 3, 4, 5, 6, name='John', age=25)
# 输出:
# a=1, b=3
# args: (4, 5, 6)
# kwargs: {'name': 'John', 'age': 25}
2. 函数也是一种对象:一等函数与高阶函数
在 Python 中,函数是第一类对象。这意味着函数可以:
被赋值给一个变量
作为参数传递给另一个函数
作为另一个函数的返回值
2.1 把函数赋值给变量
def say_hello(name):
return f"Hello, {name}!"
greet = say_hello # 将函数对象赋值给变量 greet
print(greet("World")) # 输出: Hello, World!
2.2 高阶函数
接收函数作为参数,或者返回函数作为结果的函数,被称为高阶函数。map
, filter
, sorted
等都是经典的高阶函数。
# 1. 作为参数
def apply_func(func, data):
return [func(item) for item in data]
numbers = [1, 2, 3, 4]
squared = apply_func(lambda x: x**2, numbers)
print(squared) # 输出: [1, 4, 9, 16]
# 2. 作为返回值
def get_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = get_multiplier(2)
triple = get_multiplier(3)
print(double(5)) # 输出: 10
print(triple(5)) # 输出: 15
2.3 Lambda 匿名函数
lambda
关键字用于创建小巧的匿名函数。它只是一个表达式,函数体比 def
简单的多。
# 语法: lambda arguments: expression
square = lambda x: x ** 2
print(square(5)) # 输出: 25
# 常用于高阶函数中作为参数
numbers = [1, 4, 2, 9, 5]
sorted_numbers = sorted(numbers, key=lambda x: -x)
print(sorted_numbers) # 输出: [9, 5, 4, 2, 1]
3. 迭代器 (Iterator) 与生成器 (Generator)
3.1 迭代器 (Iterator)
迭代器是实现了迭代器协议的对象,该协议包含两个方法:
__iter__()
: 返回迭代器对象本身。__next__()
: 返回容器的下一个元素。如果没有更多元素,则抛出StopIteration
异常。
列表、元组、字典、集合都是可迭代对象 (Iterable),但不是迭代器。可以使用 iter()
函数将它们转换为迭代器。
my_list = [1, 2, 3]
my_iter = iter(my_list) # 获取迭代器
print(next(my_iter)) # 输出: 1
print(next(my_iter)) # 输出: 2
print(next(my_iter)) # 输出: 3
# print(next(my_iter)) # 抛出 StopIteration 异常
3.2 生成器 (Generator)
生成器是一种特殊的迭代器,它延迟计算,按需产生值,而不是一次性在内存中构建整个序列,非常节省内存。
创建生成器有两种主要方法:
方法一:生成器函数 (使用 yield
)
使用 yield
关键字代替 return
。每次调用 next()
时,函数会从上次 yield
的位置继续执行。
def countdown(n):
print("Starting countdown!")
while n > 0:
yield n # 产生值 n,并在此暂停
n -= 1
print("Blastoff!")
# 创建生成器对象
cd = countdown(3)
print(next(cd)) # 输出: Starting countdown! 然后输出 3
print(next(cd)) # 输出: 2
print(next(cd)) # 输出: 1
print(next(cd)) # 输出: Blastoff! 然后抛出 StopIteration
方法二:生成器表达式
语法类似列表推导式,但使用圆括号 ()
。
# 列表推导式 - 立即生成所有数据,耗内存
list_comp = [x*x for x in range(1000000)]
# 生成器表达式 - 几乎不占内存,按需生成
gen_exp = (x*x for x in range(1000000))
print(next(gen_exp)) # 输出: 0
print(next(gen_exp)) # 输出: 1
# ... 可以用于 for 循环
for num in gen_exp:
if num > 25:
break
print(num, end=' ')
# 输出: 4 9 16 25
4. 常用内置函数 (Built-in Functions)
Python 提供了许多高效的内置函数,以下是一些非常常用的:
len(s)
: 返回对象长度。range(stop) / range(start, stop[, step])
: 生成一个整数序列。type(obj)
: 返回对象的类型。isinstance(obj, classinfo)
: 检查对象是否是某个类或其子类的实例。input([prompt])
: 从标准输入读取字符串。print(*objects, sep=' ', end='\n')
: 打印输出。enumerate(iterable, start=0)
: 返回一个枚举对象,生成 (index, value) 元组。zip(*iterables)
: 将多个可迭代对象中对应的元素打包成元组。map(func, iterable, ...)
: 将函数应用于可迭代对象的每个元素,返回一个迭代器。sorted(iterable, key=None, reverse=False)
: 返回一个新的排序列表。
5. 闭包 (Closure)
闭包是一个能够访问其外部函数作用域中变量的内部函数,即使外部函数已经执行完毕。
闭包的三个必要条件:
必须有一个嵌套函数(内部函数)。
嵌套函数必须引用外部函数中定义的变量。
外部函数必须返回内部函数。
def outer_func(msg):
message = msg # 外部作用域的变量
def inner_func(): # 嵌套函数
print(message) # 引用了外部变量 message
return inner_func # 返回内部函数对象
my_closure = outer_func("Hello, Closure!")
my_closure() # 输出: Hello, Closure!
# 此时 outer_func 已执行完,但 inner_func 仍能记住并访问 message 变量
闭包是一种强大的技术,常用于数据隐藏和封装、装饰器和回调函数等场景。但闭包也有一个缺陷,外部函数的非局部变量一直被内部函数引用,长时间得不到回收,可能会导致内存泄漏,因此使用的优先级就进一步降低了。
6. 装饰器 (Decorator)
装饰器是 Python 中最具特色的功能之一。它本质上是一个接收函数作为参数并返回一个新函数的高阶函数。装饰器可以在不修改原函数代码的情况下,为其增加新的功能(如日志、计时、权限校验等)。
6.1 基本语法与原理
使用 @decorator_name
语法糖放在函数定义前。
def my_decorator(func): # 接收一个函数作为参数
def wrapper(): # 定义一个包装函数
print("Something is happening before the function is called.")
func() # 执行原函数
print("Something is happening after the function is called.")
return wrapper # 返回包装函数
@my_decorator # 这相当于: say_hello = my_decorator(say_hello)
def say_hello():
print("Hello!")
say_hello()
# 输出:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.
6.2 处理带参数的函数
为了让装饰器通用,内部的 wrapper
函数应使用 *args
和 **kwargs
来接受任意参数。
def decorator_with_args(func):
def wrapper(*args, **kwargs):
print("Decorator is working!")
result = func(*args, **kwargs) # 将参数原封不动传给原函数
return result
return wrapper
@decorator_with_args
def greet(name):
print(f"Hello, {name}!")
greet("World")
6.3 带参数的装饰器
如果要让装饰器本身也能接收参数,就需要再嵌套一层函数。
def repeat(num_times): # 装饰器工厂,接收参数
def decorator_repeat(func): # 真正的装饰器
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result # 通常返回最后一次调用的结果
return wrapper
return decorator_repeat
@repeat(num_times=3)
def say_hi():
print("Hi!")
say_hi()
# 输出:
# Hi!
# Hi!
# Hi!
6.4 类装饰器
除了函数,类也可以作为装饰器,只需实现 __call__
方法。
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs): # 让实例变得可调用
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def example():
print("This is an example.")
example()
example()
print(f"Function was called {example.num_calls} times.")
# 输出:
# Call 1 of example
# This is an example.
# Call 2 of example
# This is an example.
# Function was called 2 times.
总结
Python 的函数远不止是简单的代码块。从灵活的参数处理,到作为一等对象的特性,再到强大的迭代器、生成器和装饰器,它们共同构成了 Python 简洁而强大的表达能力。熟练运用函数能够加快开发工作的效率,也能在一定程度上提高代码的拓展性,是python中非常实用的功能。