2013-07-15 14 views
5

sto cercando di fare i seguenti lavori cosa, ma senza successo:In python, come far funzionare l'operando di destra assume la priorità (del metodo __rmul__) quando si moltiplicano due classi diverse?

ho definito il mio tipo di Unit (eredita da costruire-in di tipo float) per implementare l'algebra per le quantità con le unità. Si fa le cose nel modo in cui:

class Unit(float): 
"""provide a simple unit converter for a given quantity""" 

    def __new__(cls, unit, num=1.): 
     return super(Unit, cls).__new__(cls, num) 

    def __init__(self, unit, num=1.): 
     """set up base unit""" 
     self.unit = unit 

    def __str__(self,): 
     return '{:s} {:s}'.format(super(Unit, self).__str__(), self.unit) 

    def __rmul__(self, other): 
     print 'rmul: {:f}'.format(super(Unit, self).__rmul__(other)) 
     return Unit(self.unit, super(Unit, self).__rmul__(other)) 

    def to(self,target): 
     fun_conv = _conv(self.unit, target) 
     return Unit(target, num=fun_conv(self)) 



c = 3e8 * Unit('m/s') # this will 1) create a Unit instance with magnitude '1' and unit 'm/s', 
         #   2) invoke __rmul__ to return a new instance with number 3e8 and unit 'm/s' to variable 'c' 
print c.to('km/s')  # returns 3e5 km/s 

Tuttavia, questo __rmul__ viene invocato solo quando float essendo l'operando sinistro. Se faccio qualcosa di simile:

velocities = np.array([20, 10]) * Unit('m/s') 

Poi Unit.__rmul__ non sarà invocato, e lo stesso numpy ndarray viene restituito dal momento che ora Unit('m/s') stato trattato come un normale float con valore di 1,0

Quello che mi aspetto è il seguente: dopo ndarray * Unit , una funzione simile a Unit.to può essere associata all'istanza di ndarray come metodo e anche a un attributo unit, quindi posso anche chiamare ndarray.to per restituire una copia (o versione modificata, se possibile, per l'efficienza della memoria) dell'originale narray associato a nuovi valori e unità. Come procedo?

Secondo quello che ho conosciuto e ricercato, __mul__ della operando a sinistra sarà del priore durante *, vale a dire, i controlli interprete LO.__mul__() primi, se non riesce, poi va a RO.__rmul__(). Non voglio assolutamente ignorare lo numpy.ndarray.__mul__ perché non so davvero quanto sarebbe complicato, e se ci sarebbe un gran casino nel caso in cui infrangesse le regole che agiscono su altri oggetti.

E, in realtà, non riesco nemmeno a trovare dove sono i codici che definiscono __mul__ per ndarray. Ho semplicemente usato inspect.getsource(np.ndarray) ma senza successo. Perché fallisce su questo? l'eccezione era a malapena uno IOError.

Grazie mille per la vostra preoccupazione!

+2

hai pensato che in scipy.constants.physical_constants, trovi anche la velocità della luce, ecc? http://docs.scipy.org/doc/scipy/reference/constants.html Anche loro hanno unità ecc., ma non so davvero quanto sia ben supportata la moltiplicazione di roba con unità ecc., ma immagino che questo potrebbe indirizzarti verso destra direzione per non reinventare la ruota se si ottiene il mio significato – usethedeathstar

+0

Ho appena fatto un rapido controllo su quello, ma sembra che nessuna conversione di magnitudine/flusso sia supportata. E sembra troppo semplice. Quello che alla fine voglio fare è rendere possibile lavorare con varietà di conversioni non lineari, così come la propagazione dell'errore o qualcosa di simile. E ora sto imparando, quindi non mi dispiacerebbe reinventare la ruota. Ma grazie lo stesso! –

+1

Questo è un altro esempio del perché ** non dovrebbe ** ereditare dai tipi predefiniti. Pensi che la loro sottoclassi sia buona, dato che puoi semplicemente modificare alcuni metodi e hai finito, ma * questo è falso *. * ogni volta * sottoclassi un built-in * devi * re-implementare * ogni * metodo, * plus * implementare i metodi '__r * __', altrimenti * ci * sarà bug. Quindi è semplicemente più semplice usare la composizione. – Bakuriu

risposta

2

Se non si esegue il float, ma si crea invece un nuovo tipo di spostamento a capo (quindi float._ mul _ (yourtype) non funziona), rmul farà ciò che si desidera. Ovviamente, il wrapping non sarà libero, tuttavia ... e dovrai implementare tutte le operazioni che vuoi che il tipo supporti.

class T(object): 
    def __init__(self, val): 
    self.val = val 

    def __mul__(self, x): 
    print("mul") 
    return T(self.val*x) 

    def __rmul__(self, x): 
    print("rmul") 
    return T(self.val*x) 

    def __repr__(self): 
    return str(self.val) 

>>> t = T(2) 
>>> t * 2 
mul 
4 
>>> 2*t 
rmul 
4