具有字典推导式的元编程

Meta programming with dictionary comprehensions

所以我有一堆函数要用不同的参数创建。其中一个参数 df 将由这些函数的调用者提供。我以为我已经弄清楚了,但是当我实际使用它时,创建的每个函数都有相同的参数,即列表理解序列中的最后一个组合。奇怪。

from itertools import product
feature_functions = {
    **{f'{col}{i}': lambda x: createFeature(df=x, i=i, col=col, name=f'{col}{i}')
        for col, i in product(['New', 'Lost', 'Change'], list(range(1, 31)))},

就像我说的,我觉得这很漂亮,但是当我这样使用它时:

feature_functions['New1'](df)

我得到了这个结果,这意味着它对每个 lambda 函数使用了 'Change' 和 30:

# feature pd.Series:
0     NaN
      ...   
4593  1.002706
Name: Change30, Length: 4594, dtype: float64

我尝试了几件事,但没有任何改变。我怎么用错了这本词典理解?

编辑:顺便说一句,我为验证它的正确性所做的一件事是将 lambda x: ... 放在引号中,然后我可以将其全部打印出来,看起来非常不错。那么,不知何故 lambda 妨碍了?我确实尝试将它包装在 (lambda x: ...) 中,但没有任何作用。

{'New1': "lambda x: createFeature(df=x, i=1, col=New, name='New1')",
 'New2': "lambda x: createFeature(df=x, i=2, col=New, name='New2')",
 'New3': "lambda x: createFeature(df=x, i=3, col=New, name='New3')",
 'New4': "lambda x: createFeature(df=x, i=4, col=New, name='New4')",
 ... 
}

你真的很亲近。在这里,不要使用 lambda,而是使用 functools 模块中的 partial 函数:

# dummy function
def createFeature(df, i, col, name):
    print(df)
    print(i, col, name)

feature_functions = {
    **{f'{col}{i}': partial(createFeature, i=i, col=col, name=f'{col}{i}')
        for col, i in product(['New', 'Lost', 'Change'], list(range(1, 31)))}}

用法:

>>> feature_functions['New1'](pd.DataFrame)
Empty DataFrame
Columns: []
Index: []
1 New New1

>>> feature_functions['Lost23'](pd.DataFrame())
Empty DataFrame
Columns: []
Index: []
23 Lost Lost23

>>> feature_functions['Change12'](pd.DataFrame())
Empty DataFrame
Columns: []
Index: []
12 Change Change12

好的,这很有趣。如果你创建一个 returns 你的 lambda 的函数,例如:

def createFeatureCreator(i, col):
   return lambda x: createFeature(df=x, i=i, col=col, name=f'{col}{i}')

并做你的理解(我删除了你的很多虚假内容):

feature_functions = {f'{col}{i}': createFeatureCreator(i, col)
        for col, i in product(['New', 'Lost', 'Change'], range(1, 31))}

它如您所愿地工作。

“lambda”构造不能直接工作的原因实际上很有趣:lambda 捕获环境。字典理解是一个单一的环境,其中变量 icol 在循环的每次迭代中都会发生变化。创建 lambda 时(实际上创建了 93 个不同的 lambda),它们都捕获相同的环境,因此当它们被执行时,icol 的值是它们在环境(f 字符串扩展为函数调用,因为它在 lambda 内部而未执行,并且仅在您实际调用该函数时执行,这就是名称也显示为“错误”的原因)。