解谜冗余乘数函数工厂解谜冗余乘数函数工厂

44 阅读2分钟

近日,Steven D'Aprano 在 comp.lang.python 上发布了一段“有趣”的代码,作为家庭作业问题的玩笑答案。以下是代码:

class MultiplierFactory(object):
    def __init__(self, factor=1):
        self.__factor = factor
    @property
    def factor(self):
        return getattr(self, '_%s__factor' % self.__class__.__name__)
    def __call__(self, factor=None):
        if not factor is not None is True:
            factor = self.factor
        class Multiplier(object):
            def __init__(self, factor=None):
                self.__factor = factor
            @property
            def factor(self):
                return getattr(self,
                '_%s__factor' % self.__class__.__name__)
            def __call__(self, n):
                return self.factor*n
        Multiplier.__init__.im_func.func_defaults = (factor,)
        return Multiplier(factor)

twice = MultiplierFactory(2)()

我们知道 twice 函数等同于以下答案:

def twice(x):
    return 2*x

MultiplierMultiplierFactory 的名称中,我们可以猜测代码的功能,但我们无法确定其确切的内部机制。让我们先对其进行简化。

2、解决方案

逻辑

if not factor is not None is True:
    factor = self.factor

not factor is not None is True 等价于 not factor is not None, 也即 factor is None. 结果:

if factor is None:
    factor = self.factor

到目前为止,这都很容易理解 :)

属性访问

另一个有趣的地方是好奇的 factor 访问器。

def factor(self):
    return getattr(self, '_%s__factor' % self.__class__.__name__)

MultiplierFactory 的初始化过程中,设置 self.__factor。但稍后,代码访问了 self.factor

然后似乎:

getattr(self, '_%s__factor' % self.__class__.__name__)

正在执行的操作“self.__factor”。

我们是否总是能以这种方式访问属性?

def mygetattr(self, attr):
    return getattr(self, '_%s%s' % (self.__class__.__name__, attr))

动态改变函数签名

总之,以下为简化的代码:

class MultiplierFactory(object):
    def __init__(self, factor=1):
        self.factor = factor
    def __call__(self, factor=None):
        if factor is None:
            factor = self.factor
        class Multiplier(object):
            def __init__(self, factor=None):
                self.factor = factor
            def __call__(self, n):
                return self.factor*n
        Multiplier.__init__.im_func.func_defaults = (factor,)
        return Multiplier(factor)

twice = MultiplierFactory(2)()

现在代码几乎清晰了。唯一令人费解的可能是:

Multiplier.__init__.im_func.func_defaults = (factor,)

那里有什么?我在datamodel文档中找到 func_defaults 是“包含具有默认值的那些参数的默认参数值的元组,如果没有参数具有默认值,则为 None”。我们是否在此处仅更改 __init__factor 参数的默认值?结果代码将是:

class MultiplierFactory(object):
    def __init__(self, factor=1):
        self.factor = factor
    def __call__(self, factor=None):
        if factor is None:
            factor = self.factor
        class Multiplier(object):
            def __init__(self, innerfactor=factor):
                self.factor = innerfactor
            def __call__(self, n):
                return self.factor*n
        return Multiplier(factor)

twice = MultiplierFactory(2)()

这意味着动态设置默认值毫无用处,因为 Multiplier 从未在没有默认参数的情况下被调用,对吗?

我们可能可以将其简化为:

class MultiplierFactory(object):
    def __init__(self, factor=1):
        self.factor = factor
    def __call__(self, factor=None):
        if factor is None:
            factor = self.factor
        def my_multiplier(n):
            return factor*n
        return my_multiplier

twice = MultiplierFactory(2)() # similar to MultiplierFactory()(2)

正确吗?

对于那些急于“这不是一个真正的问题”的人来说……请再读一遍,我的问题用 bold+italic 表示。

问题1:我们是否总是能以这种方式访问属性?

回答1:不。只有那些以双下划线开头的属性。它们被混淆成这种方式,以防止意外访问/从类外部覆盖。

问题2:我们在此处仅更改 __init__factor 参数的默认值吗?

回答2:是。

问题3:对吗?

回答3:对。