Question

Cette semaine sur comp.lang.python, un "intéressant" morceau de code a été publié par Steven d'Aprano comme une réponse à une question blague aux devoirs. Ici, il est:

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)() 

Nous savons que twice est équivalent à la réponse:

def twice(x):
    return 2*x

Des noms Multiplier et MultiplierFactory nous avons une idée de ce qui est le code à faire, mais nous ne sommes pas sûrs des internes exacts. Simplifions premier.

Logic

if not factor is not None is True:
    factor = self.factor

not factor is not None is True est équivalente à not factor is not None, qui est également factor is None. Résultat:

if factor is None:
    factor = self.factor

Jusqu'à présent, c'était facile:)

Attribut accès

Un autre point intéressant est le accesseur factor curieux.

def factor(self):
    return getattr(self, '_%s__factor' % self.__class__.__name__)

Lors de l'initialisation de MultiplierFactory, self.__factor est réglé. Mais plus tard, le code accède self.factor.

Il semble donc que:

getattr(self, '_%s__factor' % self.__class__.__name__)

fait exactement "self.__factor".

Peut-on accéder à toujours les attributs de cette façon?

def mygetattr(self, attr):
    return getattr(self, '_%s%s' % (self.__class__.__name__, attr))

La modification dynamique des signatures de fonction

Quoi qu'il en soit, à ce point, voici le code simplifié:

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)() 

Le code est maintenant presque propre. La seule ligne déroutante, peut-être, serait:

Multiplier.__init__.im_func.func_defaults = (factor,)

Qu'est-ce qui est là-dedans? Je regardais le datamodel doc , et a constaté que func_defaults était « un tuple contenant des arguments par défaut pour les arguments qui ont par défaut, ou aucun si aucun argument ont une valeur par défaut ». Est-ce que nous venons de changer la valeur par défaut pour factor argument __init__ ici code résultant serait alors:

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)() 

Ce qui signifie que la définition dynamique de la valeur par défaut était juste le bruit inutile, puisque Multiplier est jamais appelé sans paramètre par défaut, droite

Et nous pourrions simplifier probablement à:

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?

Et pour ceux qui se presser à "ce n'est pas une vraie question" ... lire à nouveau, mes questions sont en gras + italique

Était-ce utile?

La solution

Q1. Peut-on accéder à toujours les attributs de cette façon?

A: Non, c'est que les attributs qui commencent avec double underscores. Ils se brouillées de cette façon, pour empêcher l'accès accidentel / remplaçant l'extérieur de la classe.

Q2: changeons-nous simplement la valeur par défaut pour argument facteur __init__ ici

A: Oui.

Q2: droite

? Droit

.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top