具有“动态常数”的Python工厂函数

时间:2019-10-20 17:40:51

标签: python scope factory-method

最小示例

下面的ME可能有点太小了,因为可以用其他方式解决它,但这并不能解决我的真正问题。无论如何,我还是从这个ME入手,因为我觉得这将问题归结为我对Python缺乏了解。

假设我想要一个工厂函数来创建一堆动态函数。我已将问题简化为以下最小示例:

def dynamic_functions(constants):
    d = {}
    for c in constants:
        d[c] = lambda x: c * x

    return d

我现在得到以下信息:

>>> fn_2 = dynamic_functions(constants=[2, 3])[2]
>>> fn_2(10)  # I would want to get `20`
30

我开始理解这是因为lambda函数中的变量c不在dict理解期间求值,而是在调用该函数时求值。此时,c的值为3,因为这是它在for循环中拥有的最后一个值。 (顺便说一句,如果我将lambda函数转换为命名函数定义,这种情况不会改变。)这种理解得到了这样的事实的支持,即在如下的for循环之后添加del c

def dynamic_functions(constants):
    d = {}
    for c in constants:
        d[c] = lambda x: c * x

    del c  # NB that this line has been added.

    return d

提高NameError: free variable 'c' referenced before assignment in enclosing scope

如何解决这个问题并创建一个我想要做的工厂,即动态创建具有任意但固定常量的函数?

我的真正问题

当然,最小的示例本身可以通过简单地编写一个带有两个参数cx的函数来解决。但是我想解决我了解的范围问题,因为在我的实际用例中,我想基于传递给工厂的参数在分解类上定义属性。因此,我的用例更加复杂,并且无法解决上述ME的问题。 (或者是?我将很高兴学习如何使用。)

这是我真正的问题:

def Structure(params):
    properties = {
        param_name: property(
            fget=lambda self: getattr(self, f"_{param_name}"),
            fset=lambda self, value: setattr(self, f"_{param_name}", value)
        )
        for param_name in params
    }

    def __init__(self, **kwargs):
        for parameter in params:
            setattr(self, f"_{parameter}", kwargs.get(parameter))

    methods = {
        "__init__": __init__
    }

    _Structure = type(
        "some_name", (),
        {
            **methods,
            **properties
        }
    )

    return _Structure
>>> Struct = Structure(params=["x", "y"])
>>> struct = Struct(x=1, y=2)
>>> struct._x
1  #  That's correct
>>> struct.x
2  # That is actually the value of struct._y

这仍然是我的问题的简化代码版本,我的实际用例具有更复杂的getter和setter。

如何解决这个问题并创建一个我想要做的工厂,即动态创建具有动态属性的类?

0 个答案:

没有答案