2013-07-15 11 views
5

Próbuję zrobić następujące prace rzecz, ale bez powodzenia:W python, jak ustawić prawy operand ma priorytet (z metody __rmul__) podczas mnożenia dwóch różnych klas?

I zdefiniowany własny typ Unit (dziedziczą z wbudowanym typu float) w celu wdrożenia algebry dla ilości z jednostkami. Robi rzeczy w ten sposób, że:

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 

Jednak ta __rmul__ jest wywoływana tylko wtedy, gdy float jest lewy argument. Jeśli zrobię coś takiego:

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

Następnie Unit.__rmul__ nie zostanie wywołana, a tym samym numpy ndarray jest zwracany od teraz Unit('m/s') był traktowany jak zwykły float o wartości 1,0

Czego oczekuję jest po ndarray * Unit , funkcja podobna do Unit.to może zostać rozpoznana jako instancja ndarray jako metoda, a także jako atrybut unit, więc mogę dalej wywoływać ndarray.to, aby zwrócić kopię (lub zmodyfikowaną wersję, jeśli mogłaby, dla wydajności pamięci) oryginału ndarray związany z nowymi wartościami i jednostką. Jak mogę kontynuować?

Według tego, co znam i szukał, __mul__ lewego argumentu będzie uprzednia podczas *, to znaczy sprawdza tłumacza LO.__mul__() pierwsze, jeśli to się nie powiedzie, następnie przechodzi do RO.__rmul__(). Nie chcę zupełnie przesłonić numpy.ndarray.__mul__, ponieważ naprawdę nie wiem, jak skomplikowane byłoby to i czy byłby wielki bałagan w przypadku, gdyby łamał zasady, które działają na innych obiektach.

A tak naprawdę nawet nie mogę znaleźć kodów, które definiują __mul__ dla ndarray. Po prostu użyłem inspect.getsource(np.ndarray), ale bez powodzenia. Dlaczego to się nie udaje? wyjątek był zaledwie IOError.

Dziękuję bardzo za troskę!

+2

Czy uważasz, że w scipy.constants.physical_constants, możesz znaleźć prędkość światła itp.? http://docs.scipy.org/doc/scipy/reference/constants.html Oni też mają jednostki itp., ale ja naprawdę nie wiem jak dobrze obsługiwane są pomnożenie rzeczy z jednostkami itd., ale myślę, że to może wskazywać ci na prawo Kierunek, aby nie odkrywać koła, jeśli masz na myśli – usethedeathstar

+0

Po prostu szybko to sprawdziłem, ale wygląda na to, że nie jest możliwa konwersja wielkości/strumienia. I wydaje się zbyt proste. Ostatecznie chcę umożliwić pracę z różnymi nieliniowymi konwersjami, a także propagację błędów lub coś podobnego. I teraz się uczę, więc nie miałbym nic przeciwko temu, żeby wymyślić nowe koło. Ale dziękuję ci tak samo! –

+1

Jest to inny przykład, dlaczego ** nie powinno się ** dziedziczyć z wbudowanych typów. Myślisz, że ich podklasowanie jest dobre, ponieważ możesz po prostu zmodyfikować niektóre metody i gotowe, ale * to jest fałsz *. * za każdym razem * podklasę wbudowanego użytkownika * trzeba * ponownie zaimplementować * każdą * metodę, * plus * zaimplementować metody '__r * __', w przeciwnym razie * będą * robaki. W związku z tym po prostu łatwiej jest użyć kompozycji. – Bakuriu

Odpowiedz

2

Jeśli nie masz nic przeciwko float, ale zamiast tego utwórz nowy typ zawijania paczki (tak float._ mul _ (twój typ) nie działa), rmul zrobi to, co chcesz. Zawijanie oczywiście nie będzie darmowe, ale ... i będziesz musiał wdrożyć wszystkie operacje, które chcesz, aby dany typ obsługiwał.

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