Pergunta

Esta semana em comp.lang.python, uma peça de código "interessante" foi publicado Por Steven D'Aprano como uma piada, responde a uma pergunta de lição de casa. Aqui 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)() 

Nós sabemos isso twice é um equivalente à resposta:

def twice(x):
    return 2*x

Dos nomes Multiplier e MultiplierFactory Temos uma idéia do que o código está fazendo, mas não temos certeza dos internos exatos. Vamos simplificar primeiro.

Lógica

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

not factor is not None is True é equivalente a not factor is not None, Que tambem é factor is None. Resultado:

if factor is None:
    factor = self.factor

Até agora, isso foi fácil :)

Acesso ao atributo

Outro ponto interessante é o curioso factor acessador.

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

Durante a inicialização de MultiplierFactory, self.__factor está definido. Mas mais tarde, o código acessa self.factor.

Parece então:

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

Está fazendo exatamente "self.__factor".

Podemos sempre acessar atributos dessa maneira?

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

Mudança dinamicamente assinaturas de funções

De qualquer forma, neste momento, aqui está o código simplificado:

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

O código está quase limpo agora. A única linha intrigante, talvez, seria:

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

O que há aí? Eu olhei para o DataModel Doc, e encontrei isso func_defaults foi "Uma tupla contendo valores de argumento padrão para os argumentos que têm padrões, ou nenhum se nenhum argumento tiver um valor padrão". Estamos apenas mudando o valor padrão para factor argumento em __init__ aqui? O código resultante seria então:

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

O que significa que definir dinamicamente o valor padrão era apenas um ruído inútil, pois Multiplier nunca é chamado sem um parâmetro padrão, certo?

E provavelmente poderíamos simplificar para:

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)

Correto?

E para aqueles que se apressam para "esta não é uma pergunta real" ... Leia novamente, minhas perguntas estão em negrito+itálico

Foi útil?

Solução

Q1. Podemos sempre acessar atributos dessa maneira?

R: Não. São apenas os atributos que começam com sublinhados duplos. Eles são ofuscados dessa maneira, para evitar acesso/substituição acidental de fora da classe.

Q2: Estamos apenas alterando o valor padrão do argumento fatorial em __init__ aqui?

A: Sim.

Q2: Certo?

Certo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top