Pregunta

Esta semana en comp.lang.python, una pieza "interesante" del código fue publicado por Steven D'Aprano como una respuesta a una pregunta broma tarea. Aquí 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)() 

Sabemos que twice es un equivalente a la respuesta:

def twice(x):
    return 2*x

A partir de los nombres Multiplier y MultiplierFactory nos hacemos una idea de lo que está haciendo el código, pero no estamos seguros de las partes internas exactas. Vamos a simplificar primero.

Lógica

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

not factor is not None is True es equivalente a not factor is not None, que también se factor is None. Resultado:

if factor is None:
    factor = self.factor

Hasta ahora, eso fue fácil:)

Atributo de acceso

Otro punto interesante es la curiosidad de acceso factor.

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

Durante la inicialización de MultiplierFactory, se establece self.__factor. Pero más tarde, el código de acceso a self.factor.

A continuación, parece que:

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

está haciendo exactamente "self.__factor".

¿Se puede acceder a los atributos siempre de esta manera?

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

cambiar dinámicamente las firmas de función

De todos modos, en este momento, aquí está el 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)() 

El código es casi limpia ahora. La línea única desconcertante, tal vez, sería la siguiente:

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

¿Qué hay ahí? Miré el modelo de datos doc , y se encontró que func_defaults era " una tupla que contiene valores de los argumentos predeterminados para los argumentos que tienen valores por defecto, o None si no hay argumentos tienen un valor por defecto ". ¿Estamos simplemente cambiando el valor predeterminado de factor argumento a __init__ aquí código resultante sería entonces:?

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

Lo que significa que configurar dinámicamente el valor predeterminado era sólo ruido inútil, ya que Multiplier nunca es llamado sin un parámetro por defecto, derecho

Y que probablemente podría simplificar a:

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)

correcta?

Y para aquellos corriendo a "esto no es una cuestión real" ... leer más, mis preguntas son en negrita cursiva +

¿Fue útil?

Solución

Q1. Siempre podemos acceder a los atributos de esta manera?

R: No. Es sólo aquellos atributos que comienzan con subrayados dobles. Ellos se ofuscado de esa manera, para evitar el acceso accidental / anulando desde fuera de la clase.

P2: ¿Estamos simplemente cambiando el valor por defecto para el argumento factor en __init__ aquí

R: Sí.

P2: la derecha

?

derecho.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top