和我一起解密那个混淆的 MultiplierFactory
本周在 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
从名称 Multiplier
和 MultiplierFactory
我们知道代码是什么正在做,但我们不确定确切的内部结构。我们先来简化一下。
逻辑
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
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.