一、什么是柯里化?
柯里化(Currying) 是一种函数式编程技术,它将一个接受多个参数的函数,转换为一系列接受单个参数的函数的链条。简单来说,柯里化可以把 f(a, b, c) 变成 f(a)(b)(c)。
# 普通函数
defadd(a, b, c):
return a + b + c
# 柯里化后的函数
defcurried_add(a):
definner(b):
definner2(c):
return a + b + c
return inner2
return inner
# 使用
print(add(1, 2, 3)) # 6(普通调用)
print(curried_add(1)(2)(3)) # 6(柯里化调用)
二、为什么需要柯里化?
2.1 参数复用(固定参数)
柯里化允许你固定函数的部分参数,生成更具体的函数。
# 普通方式
defpower(base, exponent):
return base ** exponent
# 每次调用都需要传入两个参数
print(power(2, 3))
print(power(2, 4))
# 柯里化方式
defpower_curried(base):
definner(exponent):
return base ** exponent
return inner
# 固定 base=2,创建一个专门的平方函数
power_of_2 = power_curried(2)
print(power_of_2(3)) # 8
print(power_of_2(4)) # 16
2.2 延迟执行
柯里化的函数可以一部分一部分地接收参数,直到所有参数都准备好才执行。
defadd_curried(a):
definner(b):
definner2(c):
return a + b + c
return inner2
return inner
# 分步传递参数
step1 = add_curried(10) # 固定第一个参数
step2 = step1(20) # 固定第二个参数
result = step2(30) # 传递最后一个参数,执行计算
print(result) # 60
# 也可以一次性调用
print(add_curried(10)(20)(30))
2.3 函数组合的基础
柯里化函数更容易被组合,形成复杂的数据处理管道。
三、实现柯里化
3.1 手动实现柯里化函数
defcurry(func):
"""将普通函数转换为柯里化函数"""
defcurried(*args):
iflen(args) >= func.__code__.co_argcount:
# 参数数量足够,直接调用
return func(*args)
else:
# 参数不足,返回新函数接收剩余参数
returnlambda *more: curried(*(args + more))
return curried
@curry
defadd(a, b, c):
return a + b + c
print(add(1, 2, 3)) # 6
print(add(1)(2)(3)) # 6
print(add(1, 2)(3)) # 6
print(add(1)(2, 3)) # 6
3.2 使用 functools.partial 实现部分应用
Python 的 functools.partial 虽然严格来说不是柯里化,但可以实现类似的功能——固定部分参数。
from functools import partial
defpower(base, exponent):
return base ** exponent
# 固定 base 参数
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25(5²)
print(cube(5)) # 125(5³)
四、柯里化的实际应用
4.1 日志函数
deflog(level, message, timestamp):
returnf"[{timestamp}] {level}: {message}"
# 普通方式
import time
print(log("INFO", "系统启动", time.time()))
# 柯里化方式
defcurried_log(level):
definner(message):
definner2(timestamp):
returnf"[{timestamp}] {level}: {message}"
return inner2
return inner
# 创建特定级别的日志函数
info_log = curried_log("INFO")
error_log = curried_log("ERROR")
print(info_log("系统启动")(time.time()))
print(error_log("连接失败")(time.time()))
4.2 数据验证器
defvalidate(validator, value):
return validator(value)
# 柯里化版本
defcurried_validate(validator):
definner(value):
return validator(value)
return inner
# 创建专用验证器
is_positive = curried_validate(lambda x: x > 0)
is_even = curried_validate(lambda x: x % 2 == 0)
print(is_positive(5)) # True
print(is_positive(-3)) # False
print(is_even(4)) # True
4.3 HTTP 请求构建
defrequest(method, url, headers, body):
return {"method": method, "url": url, "headers": headers, "body": body}
defcurried_request(method):
definner(url):
definner2(headers):
definner3(body):
return request(method, url, headers, body)
return inner3
return inner2
return inner
# 创建特定方法的客户端
get = curried_request("GET")
post = curried_request("POST")
# 创建特定 URL 的函数
get_api = get("/api/users")
post_api = post("/api/users")
# 最终调用
print(get_api({"Accept": "application/json"})(None))
print(post_api({"Content-Type": "application/json"})({"name": "张三"}))
4.4 折扣计算器
defapply_discount(price, rate, tax_rate):
discounted = price * (1 - rate)
total = discounted * (1 + tax_rate)
returnround(total, 2)
# 柯里化版本
defcurried_discount(price):
definner(rate):
definner2(tax_rate):
return apply_discount(price, rate, tax_rate)
return inner2
return inner
# 创建特定商品价格的函数
iphone_price = curried_discount(6999)
macbook_price = curried_discount(12999)
# 应用不同折扣率
iphone_10off = iphone_price(0.1)
macbook_15off = macbook_price(0.15)
# 应用不同税率
print(iphone_10off(0.06)) # 6999 * 0.9 * 1.06
print(macbook_15off(0.06)) # 12999 * 0.85 * 1.06
4.5 配置工厂
defconnect(host, port, username, password):
returnf"Connecting to {host}:{port} as {username}"
defcurried_connect(host):
definner(port):
definner2(username):
definner3(password):
return connect(host, port, username, password)
return inner3
return inner2
return inner
# 创建特定主机的连接函数
connect_local = curried_connect("localhost")
connect_prod = curried_connect("prod.example.com")
# 创建特定端口的连接函数
connect_local_8080 = connect_local(8080)
connect_prod_443 = connect_prod(443)
# 使用
print(connect_local_8080("admin")("secret"))
print(connect_prod_443("deploy")("deploy_pass"))
五、进阶:自动柯里化
使用装饰器实现任意函数的自动柯里化。
from functools import wraps
defauto_curry(func):
"""自动柯里化装饰器"""
@wraps(func)
defcurried(*args):
iflen(args) >= func.__code__.co_argcount:
# 参数足够,调用原函数
return func(*args)
else:
# 参数不足,返回接收剩余参数的函数
returnlambda *more: curried(*(args + more))
return curried
@auto_curry
defadd(a, b, c):
return a + b + c
@auto_curry
defmultiply(a, b):
return a * b
# 多种调用方式
print(add(1, 2, 3)) # 6
print(add(1)(2)(3)) # 6
print(add(1, 2)(3)) # 6
print(add(1)(2, 3)) # 6
# 创建部分应用函数
add_10 = add(10)
add_10_20 = add_10(20)
print(add_10_20(30)) # 60
double = multiply(2)
print(double(5)) # 10
print(double(10)) # 20
六、柯里化 vs 部分应用
虽然概念不同,但实际使用中常被混用。
from functools import partial
defadd(a, b, c):
return a + b + c
# 柯里化
curried_add = lambda a: lambda b: lambda c: a + b + c
print(curried_add(1)(2)(3)) # 6
# 部分应用
add_1_and_2 = partial(add, 1, 2)
print(add_1_and_2(3)) # 6
七、总结
核心要点:
- • 柯里化将多参数函数转换为单参数函数链条:
f(a, b, c) → f(a)(b)(c)。 - • Python 中没有内置柯里化,但可以用
functools.partial 实现部分应用。
记忆口诀:
普通函数多参数
柯里化成链式路
参数不足返回函数
参数足够才执行
partial 部分应用
虽非柯里亦相近
掌握柯里化,可以让你的函数更加灵活,是函数式编程中的重要工具,尤其在参数复用和延迟执行场景中非常实用。