decifrare con me che offuscato MultiplierFactory
-
18-09-2019 - |
Domanda
Questa settimana comp.lang.python, un pezzo "interessante" del codice era inviato da Steven D'Aprano come una risposta ad una domanda scherzo compiti a casa. Eccolo:
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)()
Sappiamo che twice
è un equivalente per la risposta:
def twice(x):
return 2*x
Dai nomi Multiplier
e MultiplierFactory
noi avere un'idea di ciò che è il codice di fare, ma non siamo sicuri dei internals esatte. Diciamo semplificarlo prima.
Logic
if not factor is not None is True:
factor = self.factor
not factor is not None is True
equivale a not factor is not None
, che è anche factor is None
. Risultato:
if factor is None:
factor = self.factor
Fino ad ora, è stato facile:)
accesso Attributo
Un altro punto interessante è la funzione di accesso factor
curiosi.
def factor(self):
return getattr(self, '_%s__factor' % self.__class__.__name__)
Durante l'inizializzazione di MultiplierFactory
, self.__factor
è impostato. Ma in seguito, il codice accede self.factor
.
Sembra quindi che:
getattr(self, '_%s__factor' % self.__class__.__name__)
sta facendo esattamente "self.__factor
".
Possiamo sempre accedere agli attributi in questo modo?
def mygetattr(self, attr):
return getattr(self, '_%s%s' % (self.__class__.__name__, attr))
dinamicamente cambiando le firme di funzione
In ogni caso, a questo punto, ecco il codice semplificato:
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)()
Codice è quasi pulito ora. La linea unica sconcertante, forse, potrebbe essere:
Multiplier.__init__.im_func.func_defaults = (factor,)
Cosa c'è dentro? Ho guardato il DataModel doc , e ha scoperto che func_defaults
era " una tupla contenente valori degli argomenti predefiniti per tali argomenti che hanno valori predefiniti, o None se nessun argomento hanno un valore predefinito ". stiamo solo cambiando il valore di default per factor
argomento a __init__
qui Con conseguente codice sarebbe allora:?
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)()
Il che significa che l'impostazione dinamica il valore predefinito era il rumore solo inutile, dal momento che non viene mai chiamato Multiplier
senza un parametro di default, destro
E probabilmente potremmo semplificare 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)
Una corretta?
E per coloro affrettandosi a "questa non è una vera domanda" ... leggere di nuovo, le mie domande sono in grassetto + corsivo
Soluzione
Q1. Possiamo sempre accedere agli attributi in questo modo?
A: No. E 'solo quegli attributi che iniziano con doppia sottolineatura. Ottengono offuscato in quel modo, per impedire l'accesso accidentale / sovrascrivendo dall'esterno della classe.
Q2:? Stiamo semplicemente cambiando il valore predefinito per argomento fattore __init__
qui
A: Sì.
Q2: destro
?A destra.