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.

War es hilfreich?

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top