難読化されたMultiplierFactoryを解読します
-
18-09-2019 - |
質問
今週の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
名前から 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
今まで、それは簡単でした:)
属性アクセス
もう1つの興味深い点は、好奇心が強いことです 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 Doc, 、そしてそれを見つけました func_defaults
だった "デフォルトの引数のデフォルト引数値を含むタプル、または引数がない場合はデフォルト値がない場合". デフォルト値を変更しているだけです factor
議論 __init__
ここ? 結果のコードは次のとおりです。
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)
正しい?
そして、「これは本当の質問ではない」と急いでいる人のために...もう一度読んでください、私の質問は大胆+イタリックです
解決
Q1。この方法で常に属性にアクセスできますか?
A:いいえ。ダブルアンダースコアから始まるのは属性だけです。彼らはそのように難読化され、クラスの外部からの偶発的なアクセス/オーバーライドを防ぎます。
Q2:因子引数のデフォルト値を変更しているだけですか __init__
ここ?
A:はい。
Q2:そうですか?
右。