今週の 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
今までは簡単でした:)
属性アクセス
もう 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
「デフォルトを持つ引数のデフォルトの引数値を含むタプル、または引数にデフォルト値がない場合は None 」であることがわかりました。ここで引数のデフォルト値を変更しているだけ ですか? 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)
正しい?
そして、「これは本当の質問ではない」と急いでいる人のために...もう一度読んでください、私の質問は太字+斜体で表示されています