时间:2022-08-18 11:57:27 | 栏目:Python代码 | 点击:次
要实现的目标,简单示例:
from functools import partial def func1(f): return f def func2(f1): return f1 def func(n): return n p = partial(func2, partial(func1, partial(func, 5))) print(p()()()) # 输出5
简化嵌套式的partial对象p,不要调用三次
p() # 输出5
可以到最后的看解决方法
场景:
为了实现一个通用性较高的sql生成方法,我写了一个通用的转换时间格式的方法,简略版大概如下:
def date_trunc(time_unit: str, field): return f'date_trunc("{time_unit}", `{field}`)' print(date_trunc("WEEK", "event_date")) print(date_trunc("DAY", "event_date")) ...
实际就是sql
中的date_trunc
方法
输出:
date_trunc("WEEK", `event_date`)
date_trunc("DAY", `event_date`)
由于校验日期参数和日期截断是前后挨着执行的,我把上面的几个方法写进了一个Enum
对象TimeFormatter
使用partial将date_trunc方法包起来以充当Enum的成员,实现用Enum类校验日期参数,用Enum
类成员的date_trunc方法执行日期截断
这样在校验完日期参数后立马调用它本身的date_trunc方法执行日期截断:执行日期截断date_trunc
方法时需要传入参数time_unit
,也就是"DAY", “WEEK
”, "MONTH
"等字符串
from enum import Enum from functools import partial def date_trunc(time_unit: str, field): # 注意这里的date_trunc和上面简略版举例的有所不同,需要两个参数 return f'date_trunc("{time_unit}", `{field}`)' class TimeFormatter(Enum): DAY = partial(date_trunc, "DAY") WEEK = partial(date_trunc, "WEEK") MONTH = partial(date_trunc, "MONTH") def __call__(self, *args, **kwargs): return self.value(*args, **kwargs)
这里的call方法让Enum
对象TimeFormatter
的成员变得可以被调用(callable),关于Enum的一些用法可以参考这篇文章
到这里我依然可以正常调用我的date_trunc
方法
field = "event_time" tf_wk = TimeFormatter.__getattr__("WEEK") # 先校验格式 print(tf_wk(field)) # 传入相应的field对象就会执行对应的date_trunc方法截断时间 tf_day = TimeFormatter.__getattr__("DAY") # 校验格式 print(tf_day(field)) # 执行date_trunc
输出:
date_trunc("WEEK", `event_time`)
date_trunc("DAY", `event_time`)
直到我想要使用二次的时间格式转换时,也就是在date_trunc
之后再执行一个from_timestamp
将sql中的日期对象event_time转换为指定的"yyyy-MM-dd"格式
from_timestamp(date_trunc("DAY", `event_time`), "yyyy-MM-dd")
发现好像没那么顺利地执行时间格式转换:
from enum import Enum from functools import partial def from_timestamp(field, time_fmt: str): return f'from_timestamp(`{field}`, "{time_fmt}")' class TimeFormatter(Enum): HOUR = partial(from_timestamp, partial(date_trunc, "HOUR")) def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) tf_hour = TimeFormatter.__getattr__("HOUR") print(tf_hour("event_hour"))
输出:
from_timestamp(`functools.partial(<function date_trunc at 0x000002538E45E5E0>, 'HOUR')`, "event_hour")
不是想要的结果
查了一些解决办法,有循环调用,有用组合函数(function composition)的,
最后发现可以用一个简单的方法解决:
from enum import Enum from functools import partial def date_trunc(time_unit: str, field): return f'date_trunc("{time_unit}", `{field}`)' def from_timestamp(field, time_fmt: str): return f'from_timestamp(`{field}`, "{time_fmt}")' def fts(time_fmt, time_unit, field): return from_timestamp(date_trunc(time_unit, field), time_fmt) class TimeFormatter2(Enum): month = partial(fts, "yyyy-MM", "month") def __call__(self, *args, **kwargs): return self.value(*args, **kwargs)
输出:
from_timestamp(`date_trunc("month", `acmonth`)`, "yyyy-MM")
焯!原来只要多写一个函数就可以了!
前面简单示例的解决方法:
def nested_partials(f2, f1, n): return f2(f1(n)) p = partial(nested_partials, func2, func1) print(p(5))
输出:
5