首頁 > 軟體

python 巢狀型partials的使用

2022-03-30 19:03:49

要實現的目標,簡單範例:

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

到此這篇關於python 巢狀型partials的使用的文章就介紹到這了,更多相關python 巢狀型內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com