Decypher mit mir, dass die Multiplikatorin verschleift
-
18-09-2019 - |
Frage
Diese Woche auf Comp.lang.Python war ein "interessanter" Code -Stück Gesendet von Steven d'Aprano als Witz -Antwort auf eine Hausaufgabenfrage. Hier ist es:
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)()
Wir wissen das twice
ist ein Äquivalent der Antwort:
def twice(x):
return 2*x
Aus den Namen Multiplier
und MultiplierFactory
Wir bekommen eine Vorstellung davon, was der Code tut, aber wir sind uns der genauen Interna nicht sicher. Vereinfachen wir es zuerst.
Logik
if not factor is not None is True:
factor = self.factor
not factor is not None is True
ist äquivalent zu not factor is not None
, was auch ist factor is None
. Ergebnis:
if factor is None:
factor = self.factor
Bis jetzt war das einfach :)
Attributzugriff
Ein weiterer interessanter Punkt ist der Neugierige factor
Accessor.
def factor(self):
return getattr(self, '_%s__factor' % self.__class__.__name__)
Während der Initialisierung von MultiplierFactory
, self.__factor
ist eingestellt. Aber später greift der Code zu self.factor
.
Es scheint dann:
getattr(self, '_%s__factor' % self.__class__.__name__)
Tut genau "self.__factor
".
Können wir auf diese Weise immer auf Attribute zugreifen?
def mygetattr(self, attr):
return getattr(self, '_%s%s' % (self.__class__.__name__, attr))
Dynamisch ändernde Funktionssignaturen
Wie auch immer, zu diesem Zeitpunkt hier ist der vereinfachte 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 ist jetzt fast sauber. Die einzige rätselhafte Linie wäre vielleicht:
Multiplier.__init__.im_func.func_defaults = (factor,)
Was ist da drin? Ich schaute mir das an Datamodel doc, und fand das func_defaults
war "Ein Tupel, das Standardargumentwerte für die Argumente mit Standardeinstellungen enthält, oder keine, wenn keine Argumente einen Standardwert haben". Ändern wir nur den Standardwert für factor
Argument in __init__
hier? Der resultierende Code wäre dann:
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)()
Was bedeutet, dass das dynamische Einstellen des Standardwerts nur nutzloses Rauschen war, da seitdem Multiplier
wird niemals ohne Standardparameter aufgerufen, Rechts?
Und wir könnten es wahrscheinlich vereinfachen zu:
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)
Richtig?
Und für diejenigen, die sich beeilen, "Dies ist keine echte Frage" ... Lesen Sie noch einmal, meine Fragen sind fett und italisch.
Lösung
Q1. Können wir auf diese Weise immer auf Attribute zugreifen?
A: Nein. Es sind nur die Attribute, die mit doppelten Unterstrichen beginnen. Sie werden auf diese Weise verschleiert, um einen versehentlichen Zugang/Überschreiben von außerhalb der Klasse zu verhindern.
F2: Ändern wir nur den Standardwert für das Faktorargument in Faktor in __init__
hier?
A: Ja.
F2: Richtig?
Recht.