Seite 1 von 1

Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 11:45
von nezzcarth
Hallo :)

ich hatte letztens ein Beispiel in einer anderen Sprache gesehen, wo jemand Operatoren umdefiniert hatte, sodass (zum Beispiel, aber nicht zwingend) 5 + 5 keine Addition, sondern eine Subtraktion ausführt und damit 0 ergibt. Mal abgesehen davon, dass das natürlich nicht sehr sinnvoll und eher Spielerei ist, würde ich trotzdem gerne wissen, wie man das in Python machen würde ;)

Da isinstance(5, int) True ergibt und mein bisheriges Verständnis so war, dass - wegen des Duck Typings ? - der Ablauf c.a. so sein müsste, dass bei Verwendung des Operators '+' die entsprechende __add__ Funktion des Objektes aufgerufen würde, dachte ich es würde reichen, die entsprechenden Methoden der Klasse 'int' zu überschreiben.

Folgendes veranlasst mich aber dazu anzunehmen, dass ich da irgendwas noch nicht richtig verstanden habe:

(Python 3.3)

Code: Alles auswählen

from operator import add, sub, mul, truediv
>>> isinstance(5, int)
True
class int(int):
    def __add__(self, y):
        return sub(self.numerator, y)
    def __sub__(self, y):
        return add(self.numerator, y)
    def __mul__(self, y):
        return truediv(self.numerator, y)
    def __div__(self, y):
        return mul(self.numerator, y)

>>> 5 + 6
11
>>> m, n = int(5), int(6)
>>> m + n
-1
>>> isinstance(5, int)
False
Offensichtlich habe ich da was nicht richtig verstanden - was denn?
Werden Zahlen, die man ohne Aufruf von int() an Namen bindet also nicht "unter der Haube" doch an diese übergeben?
Und wie wäre es richtig?

Vielen Dank schon mal. :)

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 11:53
von BlackJack
@nezzcarth: Literale werden nicht durch die entsprechende Funktion erstellt sondern direkt. Und bei den eingebauten Typen kann man die Attribute auch nicht überschreiben. Das was Du vorhast geht also nicht.

Code: Alles auswählen

In [1]: int.__add__ = 42
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/home/bj/<ipython-input-1-ad98d128de62> in <module>()
----> 1 int.__add__ = 42

TypeError: can't set attributes of built-in/extension type 'int'

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 11:58
von nezzcarth
@BlackJack: Danke dir für deine rasche Rückmeldung. Ist wahrscheinlich auch besser so, dass das nicht geht; sonderlich sinnvoll und "sauberem" Code zuträglich ist das ja nicht gerade ;)

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 12:01
von snafu
Du müsstest den Rückgabewerte deiner Methoden jeweils an eine neue Instanz deiner Klasse übergeben, ansonsten werden Built-In Typen zurückgeliefert, da die importierten Funktionen aus dem `operator`-Modul nicht auf magische Art wissen können, dass sie den Typen deiner eigenen Klasse zurückgeben sollen. Wenn du nicht einfach `int` überschreiben würdest, dann wäre das vielleicht nicht ganz so verwirrend. Zudem brauchst du `.numerator` nicht unbedingt, genau so wenig wie die importierten Funktionen, weil man ja direkt die Operatoren verwenden kann. Letztlich könnte dein Beispiel (gekürzt) dann so aussehen:

Code: Alles auswählen

>>> class CrazyInteger(int):
...     def __add__(self, other):
...         return CrazyInteger(self - other)
... 
>>> result = CrazyInteger(5) + CrazyInteger(6)
>>> result
-1
>>> isinstance(result, CrazyInteger)
True
EDIT: Damit das auch mit Vererbung funktioniert, ist es übrigens besser, wenn `.__add__()` die Instanz vom "eigenen" Typen etwas abstrakter erstellt:

Code: Alles auswählen

class CrazyInteger(int):
    def __add__(self, other):
        return self.__class__(self - other)
EDIT2: Achso, das Hauptanliegen der Frage war wohl eher, ob die eigene `int`-Klasse aufgerufen wird. Naja, das hatte BlackJack ja schon beantwortet...

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 12:22
von nezzcarth
@snafu
Das mit '.numerator' wusste ich nicht, danke für den Hinweis. (Ich hatte nur kurz per dir geschaut, wie ich denn dann jetzt an die Zahl rankomme, und das gefunden - dabei scheint das streng genommen eigentlich für was anderes zu sein, sehe ich gerade). Die Operatoren hatte ich importiert, weil ich mir dachte, dass ich nicht dieselben, an deren Semantik ich gerade rumfummele, verwenden kann; tatsächlich führt

Code: Alles auswählen

class int(int):
    def __add__(self, other):
        return self - other
    def __sub__(self, other):
        return self + other
dann auch zu einer Endlosrekursion.

Insgesamt wird das alles, wie du ja auch sagtest, sehr schnell sehr verwirrend, was wohl ein weiteres Indiz dafür ist, dass man das bleiben lassen sollte ;)

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 12:42
von Leonidas
Also gehen tut das wohl schon, man kann die libpython mit ctypes laden und dort die Sachen modifizieren bis sie so funktionieren. Ist aber kein Feature was die Sprache unterstützt.

Re: Operatoren umdefinieren

Verfasst: Sonntag 24. März 2013, 14:35
von snafu
Ja, das mit der Rekursion hatte ich übersehen... :oops: