"""
柯里化类装饰器 - 优化版本
主要优化:
1. 将方法处理从实例级别移到类级别
2. 使用 __slots__ 优化内存
3. 预计算参数信息
"""
import inspect
from functools import wraps
from typing import Callable, Any, Dict, List
__all__ = ['curry_class']
_class_method_info_cache = {}
def _get_method_info(method):
"""获取方法签名信息,缓存结果"""
if method not in _class_method_info_cache:
sig = inspect.signature(method)
params = list(sig.parameters.values())
required_params = []
all_param_names = []
has_varargs = False
has_varkw = False
for param in params[1:]:
if param.kind == inspect.Parameter.VAR_POSITIONAL:
has_varargs = True
elif param.kind == inspect.Parameter.VAR_KEYWORD:
has_varkw = True
else:
all_param_names.append(param.name)
if param.default == inspect.Parameter.empty:
required_params.append(param.name)
_class_method_info_cache[method] = {
'required_params': required_params,
'all_param_names': all_param_names,
'has_varargs': has_varargs,
'has_varkw': has_varkw,
}
return _class_method_info_cache[method]
class CurriedMethod:
"""
柯里化方法包装类,支持同一方法的链式参数收集
"""
__slots__ = ('func', 'instance', 'required_params', 'has_varargs',
'has_varkw', 'all_param_names', 'collected_args', 'collected_kwargs')
def __init__(self, func: Callable, instance: Any,
required_params: List[str], has_varargs: bool, has_varkw: bool,
all_param_names: List[str], collected_args=None, collected_kwargs=None):
self.func = func
self.instance = instance
self.required_params = required_params
self.has_varargs = has_varargs
self.has_varkw = has_varkw
self.all_param_names = all_param_names
self.collected_args = collected_args or []
self.collected_kwargs = collected_kwargs or {}
def _check_params_complete(self, args=None, kwargs=None):
"""检查参数是否收集完整"""
check_args = args if args is not None else self.collected_args
check_kwargs = kwargs if kwargs is not None else self.collected_kwargs
filled = set(check_kwargs.keys())
arg_index = 0
for param in self.required_params:
if param not in filled:
if arg_index < len(check_args):
filled.add(param)
arg_index += 1
return filled == set(self.required_params)
def __call__(self, *args, **kwargs):
new_args = self.collected_args + list(args)
new_kwargs = {**self.collected_kwargs, **kwargs}
is_empty_call = len(args) == 0 and len(kwargs) == 0
params_complete = self._check_params_complete(new_args, new_kwargs)
if params_complete and not (self.has_varargs or self.has_varkw):
final_kwargs = dict(new_kwargs)
arg_idx = 0
for param in self.all_param_names:
if param not in final_kwargs and arg_idx < len(new_args):
final_kwargs[param] = new_args[arg_idx]
arg_idx += 1
return self.func(self.instance, **final_kwargs)
if (self.has_varargs or self.has_varkw) and is_empty_call:
return self.func(self.instance, *new_args, **new_kwargs)
return CurriedMethod(
self.func, self.instance, self.required_params,
self.has_varargs, self.has_varkw, self.all_param_names,
new_args, new_kwargs
)
def curry_class(cls: type):
"""
柯里化类装饰器 - 优化版本
将方法处理移到类级别,避免每个实例重复处理
"""
if not inspect.isclass(cls):
raise TypeError(f"@curry_class 装饰器仅允许应用于类,当前类型为: {type(cls).__name__}")
allowed_magic_methods = {
'__call__', '__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__',
'__mod__', '__divmod__', '__pow__', '__lshift__', '__rshift__',
'__and__', '__or__', '__xor__', '__lt__', '__le__', '__eq__', '__ne__',
'__gt__', '__ge__', '__neg__', '__pos__', '__abs__', '__invert__',
'__getitem__', '__contains__', '__iadd__', '__isub__', '__imul__',
'__itruediv__', '__ifloordiv__', '__imod__', '__ipow__', '__ilshift__',
'__irshift__', '__iand__', '__ior__', '__ixor__',
}
excluded_methods = {
'__init__', '__new__', '__del__', '__getattribute__', '__getattr__',
'__setattr__', '__delattr__', '__repr__', '__str__', '__format__',
'__len__', '__bool__', '__enter__', '__exit__', '__iter__', '__next__',
'__hash__', '__dir__', '__sizeof__',
}
method_info_map = {}
for name, method in cls.__dict__.items():
if not inspect.isfunction(method):
continue
if name in excluded_methods:
continue
if name.startswith('__') and name.endswith('__') and name not in allowed_magic_methods:
continue
method_info_map[name] = _get_method_info(method)
original_init = cls.__init__
def new_init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
for name, info in method_info_map.items():
method = cls.__dict__[name]
@wraps(method)
def create_curried(meth=method, req_params=info['required_params'],
varargs=info['has_varargs'], varkw=info['has_varkw'],
all_params=info['all_param_names']):
def curried_wrapper(*args_inner, **kwargs_inner):
temp_curried = CurriedMethod(
meth, self, req_params, varargs, varkw, all_params,
list(args_inner), kwargs_inner
)
if temp_curried._check_params_complete():
return temp_curried.__call__()
return temp_curried
return curried_wrapper
setattr(self, name, create_curried())
cls.__init__ = new_init
return cls
@curry_class
class T:
"""示例类,使用 @curry_class 装饰器"""
def __init__(self):
self.result = 0
self.data = []
def add(self, a: int, b: int, c: int) -> int:
"""加法方法,将三个数相加"""
self.result = a + b + c
return self.result
def multiply(self, x: int, y: int) -> int:
"""乘法方法,将两个数相乘"""
self.result = x * y
return self.result
def sum_all(self, a: int, b: int, *c, **k) -> int:
"""求和方法,支持可变参数"""
total = a + b + sum(c)
if k:
total += sum(k.values())
self.result = total
return total
def __call__(self, x, y, z):
"""调用方法,支持柯里化"""
self.result = x * y + z
return self.result
def __add__(self, a, b):
"""加法运算符重载"""
self.result = a + b
return self.result
def __getitem__(self, index, default=None):
"""获取列表项"""
if index < len(self.data):
return self.data[index]
return default
def __lt__(self, a, b):
"""小于比较"""
return a < b
def test_curry_decorator():
"""测试用例验证"""
print("=== 测试柯里化装饰器 ===\n")
t = T()
print("[OK] 成功实例化类T")
t1 = T()
result1 = t1.add(1).add(2).add(3)
assert result1 == 6
t2 = T()
result2 = t2.add(1, 2).add(3)
assert result2 == 6
t3 = T()
result3 = t3.add(1, 2, 3)
assert result3 == 6
t4 = T()
result4 = t4.add(a=1).add(b=2).add(c=3)
assert result4 == 6
t5 = T()
result5 = t5.add(1).add(b=2, c=3)
assert result5 == 6
t6 = T()
result6 = t6.add(a=1).add(2, c=3)
assert result6 == 6
t7 = T()
result7 = t7.multiply(5).multiply(6)
assert result7 == 30
t8 = T()
result8 = t8.multiply(x=7).multiply(y=8)
assert result8 == 56
t9 = T()
result9 = t9.sum_all(1, 2)
assert result9 == 3
t10 = T()
result10 = t10.sum_all(1).sum_all(2).sum_all(3, 4).sum_all()
assert result10 == 10
t11 = T()
result11 = t11.sum_all(1, 2, 3, x=4, y=5)
assert result11 == 15
t12 = T()
result12 = t12.sum_all(1).sum_all(2, x=3).sum_all(y=4).sum_all()
assert result12 == 10
try:
@curry_class
def not_a_class():
pass
assert False
except TypeError:
pass
t14 = T()
result14 = t14.__call__(2, 3, 4)
assert result14 == 10
t15 = T()
result15 = t15.__call__(2).__call__(3).__call__(4)
assert result15 == 10
t16 = T()
result16 = t16.__add__(5).__add__(3)
assert result16 == 8
t17 = T()
t17.data = [10, 20, 30]
result17 = t17.__getitem__(2)
assert result17 == 30
t18 = T()
t18.data = [10, 20, 30]
result18 = t18.__getitem__(5, -1)
assert result18 == -1
t19 = T()
curried = t19.__call__(2)
curried = curried.__call__(3)
result19 = curried.__call__(4)
assert result19 == 10
t20 = T()
result20 = t20.__call__(x=2).__call__(3).__call__(z=4)
assert result20 == 10
print("=== 所有测试通过 ===")
if __name__ == "__main__":
test_curry_decorator()