Dualzahlenrechner

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Mittwoch 12. März 2008, 20:59

Ich habe - für den gelegentlichen Eigengebrauch - ein Minimodul geschrieben,
um mal schnell im interaktiven Modus ein paar Dualzahlen zu verrechnen.

Ja, mein Taschenrechner kann das auch (aber nicht mit so großen Zahlen ...) und ganz bestimmt gibt es schon fertige Module, die das können.

Mir geht es darum, ob die Art, wie ich es umgesetzt habe, "Python thinking" ist. Macht der Pythologe an sich das so?

Code: Alles auswählen

# dual.py
# Grundrechenarten mit Dualzahlen

class Dual(object):

    def __init__(self,value):
        self.value = int(value,2) if isinstance(value,str) else value
        if isinstance(self.value,float):
            raise ValueError # float is not supported

    def __str__(self):
        sgn = "-" if self.value<0 else ""
        bin = "" if self.value!=0 else "0"
        dec = abs(self.value)
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __add__(self,bin):
        return Dual(self.value+bin.value)

    def __sub__(self,bin):
        return Dual(self.value-bin.value)

    def __mul__(self,bin):
        return Dual(self.value*bin.value)

    def __div__(self,bin):
        return Dual(self.value//bin.value)

    def __mod__(self,bin):
        return Dual(self.value%bin.value)

if __name__ == "__main__":
    z1 = Dual(13)
    z2 = Dual(5)
    print "%s + %s = %s" %(z1,z2,z1+z2)
    print "%s - %s = %s" %(z1,z2,z1-z2)
    print "%s * %s = %s" %(z1,z2,z1*z2)
    print "%s / %s = %s" %(z1,z2,z1/z2)
    print "%s %% %s = %s" %(z1,z2,z1%z2)
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

Mittwoch 12. März 2008, 22:56

Also die ganzen isinstance sind bestimmt nicht "Python thinking". Was passiert wenn dein übergebenens Objekt weder ein int noch ein float noch ein String ist? Z.B. ein unicode-Objekt. Schreib doch einfach nur self.value = int(value) und fertig.
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Mittwoch 12. März 2008, 23:28

Könnte man nicht irgendwie `int` subclassen und __str__ und __unicode__ überschreiben?

Ich knabber nur gerade daran, wie ich dem das beibringen kann...

Code: Alles auswählen

class Dual(int):
    def __new__(cls, arg="0"):
        it = int.__new__(cls, int(arg, 2))
Tja. Und nun? :oops:

€dit:

Code: Alles auswählen

class Dual(int):
    def __new__(cls, arg="0"):
        it = int.__new__(cls, int(arg, 2))
        return it

    def __str__(self):
        if self > 0:
            sgn = "+"
        else:
            sgn = "-"
        dec = abs(self)
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __unicode__(self):
        return str(self)
Nur...wie bringe ich ihn nun dazu, beim addieren wieder Dual-Objekte zurückzugeben? Jede einzelne Methode überschreiben? :(
Zuletzt geändert von audax am Mittwoch 12. März 2008, 23:41, insgesamt 1-mal geändert.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 12. März 2008, 23:38

audax hat geschrieben:Könnte man nicht irgendwie `int` subclassen und __str__ und __unicode__ überschreiben?
Um was zu erreichen? Natürlich, es ist schon möglich.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Mittwoch 12. März 2008, 23:43

Damit man eben eine hübsche Binärdarstellung bekommt....

Ich knabber nur an den Typen der Returnwerten :|

Grml:

Code: Alles auswählen

class Dual(int):
    __slots__ = []

    def __new__(cls, arg="0"):
        if isinstance(arg, int):
            value = arg
        else:
            value = int(arg, 2)
        return int.__new__(cls, value)

    def __str__(self):
        if self > 0:
            sgn = "+"
        else:
            sgn = "-"
        dec = abs(self)
        bin = ""
        while dec>0:
            bin += str(dec & 1)
            dec >>= 1
        return sgn+bin[::-1]

    def __unicode__(self):
        return str(self)

    def __repr__(self):
        return str(self)

Code: Alles auswählen

In [33]: d.Dual(d.Dual("10011") + d.Dual("1100"))
Out[33]: +11111
Man muss es eben noch in ein neues Object packen :/
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mittwoch 12. März 2008, 23:58

audax hat geschrieben:Damit man eben eine hübsche Binärdarstellung bekommt....
Ich habe mal BlackJacks Code ausgeliehen:

Code: Alles auswählen

>>> def int2bin(value):
...     result = list()
...     while value:
...         result.append(str(value & 1))
...         value >>= 1
...     result.reverse()
...     return ''.join(result).zfill(8)
...
>>> class binaryinteger(int):
...     def __repr__(self):
...         return "binaryinteger(%d)" % self
...     def __str__(self):
...         return int2bin(self)
...     def __unicode__(self):
...         return str(self).decode('ascii')
... 
>>> bi = binaryinteger(10)
>>> bi
binaryinteger(10)
>>> str(bi)
'00001010'
>>> unicode(bi)
u'00001010'
My god, it's full of CARs! | Leonidasvoice vs Modvoice
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Donnerstag 13. März 2008, 00:13

Ich hab den Code einfach pütone übernommen und nicht großartig weiter angeschaut ;)

Code: Alles auswählen

class Dual(int):
    __slots__ = []

    def __new__(cls, arg="0"):
        if isinstance(arg, int):
            value = arg
        else:
            value = int(arg, 2)
        return int.__new__(cls, value)

    def __str__(self):
        value = int(self)
        result = []
        while value:
            result.append(str(value & 1))
            value >>= 1
        result.reverse()
        return ''.join(result).zfill(8)

    def __unicode__(self):
        return str(self).decode("ascii")

    def __repr__(self):
        return "<Dual %s, %d>" % (self, self)
Bleibt immer noch das Problem der Returnwerte, dass sich wohl so einfach nicht lösen lässt...
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Donnerstag 13. März 2008, 01:41

Ist zwar nicht annähernd fertig, ausgereift oder getestet, aber hier mal ein möglicher Ansatz zur Lösung der return-Werte:

Code: Alles auswählen

def BinaryMeta(target, *the_types):
    class BinaryMetaClass(type):
        def __new__(cls, name, bases, dct):
            #update methods
            for base in bases:
                for name, func in base.__dict__.items():
                    if not name in dct:
                        dct[name] = BinaryMetaClass.create_caller(func)
            return type.__new__(cls, name, bases, dct)
        
        @staticmethod
        def has_given_type(result):
            return type(result) in the_types

        @staticmethod
        def create_caller(f):
            def caller(*args, **kwds):
                result = f(*args, **kwds)
                if BinaryMetaClass.has_given_type(result):
                    return target(result)
                return result
            
            return caller
                    

    return BinaryMetaClass

class binaryint(int):
    __metaclass__ = BinaryMeta(binaryint, int)
    
    def __repr__(self):
        return "binaryint(%d)" % self

    __str__ = __repr__


class binaryfloat(float):
    __metaclass__ = BinaryMeta(binaryfloat, float)

    def __repr__(self):
        return "binaryfloat(%f)" % self

    __str__ = __repr__


if __name__ == "__main__":
    x = binaryint(10)
    y = binaryint(20)
    z = binaryfloat(4.0)
    print x+y, x-y, x*y, x % y, x**z
    print "!!!", x**2.0, "!!!"
Ergibt:

Code: Alles auswählen

binaryint(30) binaryint(-10) binaryint(200) binaryint(10) binaryfloat(10000.000000)
!!! 100.0 !!!
Edit: Holla, auch Beispielcode mit IDLE entwickeln ist gefährlich :roll: Steckt noch ein Fehlerchen drin, da natürlich "binaryint" und "binaryfloat" zur Erzeugung der Metaklassen noch nicht bekannt sind...
audax
User
Beiträge: 830
Registriert: Mittwoch 19. Dezember 2007, 10:38

Donnerstag 13. März 2008, 02:11

Ach du meine Güte. Fieser Hack :D

Aber ne gute Idee, so klappt das immerhin schonmal
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Donnerstag 13. März 2008, 02:28

So, nun läuft es wie gewünscht:

Code: Alles auswählen

def BinaryMeta(*the_types):
    class BinaryMetaClass(type):
        def __new__(cls, cname, bases, dct):
            #update methods
            for base in bases:
                for name, func in base.__dict__.items():
                    if not name in dct:
                        dct[name] = BinaryMetaClass.create_caller(func)
            res = type.__new__(cls, cname, bases, dct)
            BinaryMetaClass.target = res
            return res
        
        @staticmethod
        def has_given_type(result):
            return type(result) in the_types

        @staticmethod
        def create_caller(f):
            def caller(*args, **kwds):
                result = f(*args, **kwds)
                if BinaryMetaClass.has_given_type(result):
                    return BinaryMetaClass.target(result)
                return result
            
            return caller
                    
    return BinaryMetaClass


def binary_creator(base, name):
    class binary(base):
        __metaclass__ = BinaryMeta(base)
        
        def __str__(self):
            return "%s(%s)" % (name, base.__str__(self))

    return binary

binaryint = binary_creator(int, "binaryint")
binaryfloat = binary_creator(float, "binaryfloat")


if __name__ == "__main__":
    x = binaryint(10)
    y = binaryint(20)
    z = binaryfloat(4.0)
    print x-y, x*y, x % y, x**z
    print "!!!", x**2.0, "!!!"
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Donnerstag 13. März 2008, 13:25

Darii hat geschrieben:Also die ganzen isinstance sind bestimmt nicht "Python thinking".
Damit kann ich nichts anfangen. Was heißt "bestimmt nicht" und vor allem: Warum nicht? Was wäre denn an dieser Stelle "Python thinking"?
Darii hat geschrieben:Was passiert wenn dein übergebenens Objekt weder ein int noch ein float noch ein String ist? Z.B. ein unicode-Objekt.
Tja, dann gibt es wohl irgendwann eine Fehlermeldung. Aber mal ehrlich:
Einen Test auf unicode kann man ja einfach noch ergänzen und alles andere dürfte wohl in der Praxis entfallen.
Darii hat geschrieben:Schreib doch einfach nur self.value = int(value) und fertig.
Das ist aber ganz schlecht. Erstens kann man dann nicht mehr Dual-Objekte durch einen Aufruf von Dual("101101") erzeugen und zweitens werden floats dann einfach zu ints glattgebügelt. Das kann doch nicht die Lösung sein.

@alle anderen:
Ich sehe in der Hauptsache, dass eure Codes immer komplizierter werden, aber ich habe nicht verstanden, was an meinem Code schlecht wäre, dass man es anders machen müsste. Für Hinweise wäre ich dankbar.

Sinnvoll ist auf jeden Fall eine Ergänzung der __repr__()-Methode. Das habe ich inzwischen nachgeholt.

Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
rabit
User
Beiträge: 11
Registriert: Freitag 1. Februar 2008, 15:01

Donnerstag 13. März 2008, 14:33

pütone hat geschrieben: Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
Welchen dualen Wert hat den zum Beispiel die Float Zahl von 2,35.
Würde mich mal interessieren. ;)

10, 100011 sicher nicht.

Grüße
rabit
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Donnerstag 13. März 2008, 14:38

Das Prinzip ist genau so wie bei Dezimalzahlen: Die erste Stelle hinter dem Komma hat die Wertigkeit 10^-1, die zweite 10^-2, usw.

Bezogen auf Binärzahlen gilt als für die erste Nachkommastelle 2^-1, für die zweite 2^-2, usw.
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 13. März 2008, 14:59

pütone hat geschrieben:
Darii hat geschrieben:Also die ganzen isinstance sind bestimmt nicht "Python thinking".
Damit kann ich nichts anfangen. Was heißt "bestimmt nicht" und vor allem: Warum nicht? Was wäre denn an dieser Stelle "Python thinking"?
Es ist nicht pythonic, weil es kein duck-typing erlaubt. Wenn es sich wie eine Zahl verhält, dann sollte sie akzeptiert werden, statt nachzuprüfen ob sie von ``int`` erbt. In Python 3 gibt es zusätzlich noch Abstract Base Classes, die einem in der Hinsicht einige weitere Freiheiten bieten.
pütone hat geschrieben:Tja, dann gibt es wohl irgendwann eine Fehlermeldung. Aber mal ehrlich:
Einen Test auf unicode kann man ja einfach noch ergänzen und alles andere dürfte wohl in der Praxis entfallen.
Warum also überhaupt Typen testen?
pütone hat geschrieben:Ich sehe in der Hauptsache, dass eure Codes immer komplizierter werden, aber ich habe nicht verstanden, was an meinem Code schlecht wäre, dass man es anders machen müsste. Für Hinweise wäre ich dankbar.
Ich wundere mich auch, was an meinem ``binaryinteger`` das Problem sein sollte, dass man Metaklassen drauf werfen müsste. Es würde wohl auch reichen ``__add__`` etc. zu wrappen, so dass sie ``binaryinteger`` zurückgeben, damit die operationen die richtigen zahlen ausgeben. Wobei, ich weiß gar nicht ob es so sinnvoll ist, überhaupt ``binaryinteger`` auszugeben.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Donnerstag 13. März 2008, 15:29

rabit hat geschrieben:
pütone hat geschrieben: Warum sollte man die Klasse Dual aber unbedingt von int ableiten?
Welchen dualen Wert hat den zum Beispiel die Float Zahl von 2,35.
Würde mich mal interessieren. ;)

10, 100011 sicher nicht.

Grüße
rabit
Wo ist denn hier ein Zusammenhang? Das eine hat doch mit dem anderen gar nichts zu tun. Im übrigen kann man meinem Code entnehmen, dass ich ja gerade keine Fließkommazahlen / Dezimalbrüche in Dualzahlen umwandeln will.

Edit: Die eingebaute Funktion hex() verarbeitet im übrigen auch keine Fließkommazahlen.

Wie ich im 1. Posting schon geschrieben habe, geht es mir nicht darum, dass ich eine andere Funktionalität wollte als der vorgestellte Code es leistet oder unzufrieden wäre mit dem, was er leistet, sondern nur darum, ob die Art und Weise, wie ich es umgesetzt habe, "python thinking" ist oder nicht.
Leonidas hat geschrieben:Warum also überhaupt Typen testen?
In meinem Original-Code verwende ich den Typentest ja in erster Linie dazu, die beiden möglichen Fälle für die Konstruktion eines Dual-Objektes zu unterscheiden, weil ich z.B. auch folgendes haben wollte:

Code: Alles auswählen

 summe = Dual(20) + Dual('101001110')
Das geht ja mit meinem Entwurf.
Leonidas hat geschrieben:Es ist nicht pythonic, weil es kein duck-typing erlaubt.
a) Verstehe ich; b) dann aber auch wieder nicht.

Aus Sicht dessen, der mit Dual() arbeitet, ist es doch duck-typing, oder nicht? Er gibt irgend eine Zahl ein und entsprechend der Art der eingegebenen Zahl wird darauf reagiert. Entweder mit einer entsprechenden Verarbeitung oder mit einem ValueError.

Nach meinem Verständnis passiert doch z.B. etwas ähnliches, wenn ich den Divisionsoperator anwende - je nach Zahlentyp funktioniert er unterschiedlich (ja, ich weiß, das wird künftig anders sein; vielleicht hängt das ja mit dem duck-typing zusammen).

Soll das heißen, dass duck-typing in diesem Fall überhaupt nur realisierbar ist, wenn ich Dual von int ableite?

Tut mir leid, falls ich begriffsstutzig bin, aber verstehen würde ich das schon gerne.
Antworten