和我一起解密那个混淆的 MultiplierFactory

发布于 2024-08-05 20:47:22 字数 4202 浏览 2 评论 0原文

本周在 comp.lang.python 上,一段“有趣”的代码是 由 Steven D'Aprano 发布,作为家庭作业问题的一个笑话答案。如下:

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 我们知道代码是什么正在做,但我们不确定确切的内部结构。我们先来简化一下。

逻辑

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,)

里面有什么?我查看了数据模型文档,发现func_defaults 是“包含具有默认值的参数的默认参数值的元组,如果没有参数具有默认值,则为 None”。 我们只是在此处更改 factor 参数的默认值 __init__ 吗? em> 结果代码将是:

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对< /em>

我们或许可以将其简化为:

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)

对吗?

对于那些急于“这不是一个真正的问题”的人...再读一遍,我的问题是粗体+斜体

This week on comp.lang.python, an "interesting" piece of code was posted by Steven D'Aprano as a joke answer to an homework question. Here it is:

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)() 

We know that twice is an equivalent to the answer:

def twice(x):
    return 2*x

From the names Multiplier and MultiplierFactory we get an idea of what's the code doing, but we're not sure of the exact internals. Let's simplify it first.

Logic

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

not factor is not None is True is equivalent to not factor is not None, which is also factor is None. Result:

if factor is None:
    factor = self.factor

Until now, that was easy :)

Attribute access

Another interesting point is the curious factor accessor.

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

During initialization of MultiplierFactory, self.__factor is set. But later on, the code accesses self.factor.

It then seems that:

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

Is doing exactly "self.__factor".

Can we always access attributes in this fashion?

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

Dynamically changing function signatures

Anyway, at this point, here is the simplified code:

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)() 

Code is almost clean now. The only puzzling line, maybe, would be:

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

What's in there? I looked at the datamodel doc, and found that func_defaults was "A tuple containing default argument values for those arguments that have defaults, or None if no arguments have a default value". Are we just changing the default value for factor argument in __init__ here? Resulting code would then be:

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)() 

Which means that dynamically setting the default value was just useless noise, since Multiplier is never called without a default parameter, right?

And we could probably simplify it to:

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)

Correct?

And for those hurrying to "this is not a real question"... read again, my questions are in bold+italic

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

旧人 2024-08-12 20:47:22

Q1.我们总能以这种方式访问​​属性吗?

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

Q2:我们只是在此处更改 __init__ 中因子参数的默认值吗?

答:是的。

Q2:对吗?

正确的。

Q1. Can we always access attributes in this fashion?

A: No. It's only those attributes who start with double underscores. They get obfuscated in that way, to prevent accidental access/overriding from outside the class.

Q2: Are we just changing the default value for factor argument in __init__ here?

A: Yes.

Q2: right?

Right.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文