Seite 1 von 2

Verfasst: Donnerstag 13. März 2008, 16:03
von Zap
Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Das ist das selbe wie bei hex. Man braucht lediglich Methoden zum konvertieren und ist IMHO glücklich:

Code: Alles auswählen

def bin(number, signed=False):
    """
    Return binary representation of an integer

    Parameters:
      - number<int>    the number to represent
      - signed<bool>   if true we have signed representation
    """
    number = int(number)
    if not number:
        return "0"
    if not signed:
        number = abs(number)
    # compute the count of needed bits
    bits = int((math.log(abs(number))/math.log(2))+1)
    bin = "".join([ "01"[number>>x & 1] for x in xrange(bits-1,-1,-1)])
    return "%s%s" % ((number < 0 and "1" or "0"), bin) 

# Die andere Richtung:
int(bin(4711), 2)

pütone hat geschrieben: 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.
Nein, Ducktyping bedeutet das es einer Klasse/Funktion egal ist von welchem Typ ein übergebenes Objekt ist. Hauptsache es verhält sich so.
In diesem fall sollte das heißen das jedes Objekt akzeptiert werden sollte das die __int__ Methode unterstützt. Egal welches Objekt dahinter steckt und wie komplex es ist.

Verfasst: Donnerstag 13. März 2008, 16:38
von numerix
Zap hat geschrieben:Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Diese Argumentation kann ich nicht nachvollziehen.
Zap hat geschrieben:Das ist das selbe wie bei hex. Man braucht lediglich Methoden zum konvertieren und ist IMHO glücklich:
Allerdings nicht mit deinem Code .... der ist nämlich fehlerhaft.

Verfasst: Donnerstag 13. März 2008, 16:43
von Leonidas
pütone hat geschrieben:
Zap hat geschrieben:Ich finde das man an den ganzen Problemen sehen kann das es nicht viel Sinn macht "Dual"/"Binary" oder wie auch immer als neuen Typen festlegen zu wollen.
Diese Argumentation kann ich nicht nachvollziehen.
Es geht darum, dass es ja immer noch ganze Zahlen sind und keine Binärzahlen. Binärzahlen sind wie Hexadezimal oder Dezimal nur eine Repräsentation von Integern.

Verfasst: Donnerstag 13. März 2008, 16:46
von Zap
Mh..

Zu 1) Da ist vielleicht Geschmacksfrage. Obwohl ein Grund fehlt mir dabei. Aber wie Leonidas schon sagt, sie sind und bleiben integer Werte und ich finde das man auch am besten mit denen Weiterarbeiten sollte.

Zu 2) wäre jetzt schön gewesen wenn du mir diesen Fehler auch bennen könntest. Für den Bereich in den ich den getestet habe funktioniert er.

Code: Alles auswählen

In [2]: for i in xrange(0, 100000):
   ...:     assert i == int(bin(i), 2)
   ...:
   ...:

In [3]:
Die Vorzeichenbehaftete Seite kann bestimmt noch ein paar Tests vertragen aber die Stichproben die ich gemacht hab waren korrekt.

Verfasst: Donnerstag 13. März 2008, 16:50
von EyDu
Die Darstellung einer Zahl kann sehr wohl einen Unterschied machen: Man bedenke beispielsweise das Bestimmen der Quersumme einer Zahl.

Das rechtfertigt meiner Meinung zwar auch noch keine eigene Klasse, aber da ich nicht weiss was pütone vor hat, hoffe ich einfach mal, dass es Sinn ergibt.

Verfasst: Donnerstag 13. März 2008, 16:53
von numerix
Leonidas hat geschrieben:Es geht darum, dass es ja immer noch ganze Zahlen sind und keine Binärzahlen. Binärzahlen sind wie Hexadezimal oder Dezimal nur eine Repräsentation von Integern.
Ja, ist klar. Aber inwiefern ich durch Dual-Objekte Probleme habe, die eine dual() oder bin()-Funktion nicht mit sich bringt, ist mir nicht klar.

Ein

Code: Alles auswählen

number = int(number)
kann ich natürlich in meinen Entwurf auch einbauen; es "löst" das Problem des Umgangs mit den floats. Aber für sinnvoll halte ich das nicht - habe ich ja in einem früheren Posting schon erläutert. Dann merke ich doch gar nicht, dass ein Dual(13.6) oder bin(13.6) oder wie auch immer gar nicht das macht, was ich vermutlich denke, dass es macht. Aber m.E. ist das auch gar nicht der Punkt.

Ich bin - für mich - immer noch nicht "fertig" mit meinem Entwurf.

Was müsste ich den konkret ändern, um duck-typing zu realisieren?
Geht das nur mit einem komplett anderen Ansatz oder sind es nur ein paar Änderungen an der __init__()-Methode?

Verfasst: Donnerstag 13. März 2008, 17:05
von numerix
EyDu hat geschrieben:..., aber da ich nicht weiss was pütone vor hat, hoffe ich einfach mal, dass es Sinn ergibt.
Ach, gar nichts Großes. Hin und wieder muss ich mit ein paar Dualzahlen rechnen und außerdem bin ich dabei, nicht nur irgendwie in Python zu programmieren, sondern das auch möglichst "pythonic" zu tun. Da kam das gerade recht, weil es klein und überschaubar ist und man eben auch sinnvoll - ganz sicher nicht zwingend - Klassen/Objekte einsetzen kann.

Ich finde es nett, wenn z.B. sowas geht:

Code: Alles auswählen

>>> from dual import *
>>> a = Dual(23)
>>> b = Dual('110101')
>>> a+b
1001100
>>> a*b
10011000011

@zap zu 2)

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'

Verfasst: Donnerstag 13. März 2008, 17:36
von audax
Hier mal der pythonischere Weg der Initialisierung:

Code: Alles auswählen

class Dual(object):

    def __init__(self,value):
        self.value = value

    @classmethod
    def fromstring(cls, value):
        return cls(
            int(value,2))

    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)

    def __rep__(self):
        return "<Dual %s-%d>" % (str(self), self.value)

if __name__ == "__main__":
    z1 = Dual.fromstring("1001")
    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)

Verfasst: Donnerstag 13. März 2008, 17:44
von EyDu

Code: Alles auswählen

def __repr__(self):
    return "Dual(%s)" % self.value
Dann tut die Methode auch das, wozu sie gedacht ist.

Verfasst: Donnerstag 13. März 2008, 17:52
von BlackJack
Mein Vorschlag zum Thema:

Code: Alles auswählen

import operator

class Dual(object):
    def __init__(self, value):
        if isinstance(value, basestring):
            value = int(value, 2)
        self.value = int(value)
    
    def __int__(self):
        return self.value
    
    def __str__(self):
        value = int(self)
        sign = '-' if value < 0 else ''
        value = abs(value)
        result = list()
        while value:
            result.append(str(value & 1))
            value >>= 1
        return sign + ''.join(reversed(result))
    
    def __repr__(self):
        return '<Dual %s:%d>' % (self, self)


def make_binary_method(cls, operation):
    def meth(self, other):
        return cls(operation(self.value, int(other)))
    return meth

for operation in ['add', 'sub', 'mul', 'div', 'truediv', 'mod']:
    setattr(Dual,
            '__%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))
    setattr(Dual,
            '__r%s__' % operation,
            make_binary_method(Dual, getattr(operator, operation)))


def 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)
    print repr(1 + Dual(15))

if __name__ == '__main__':
    main()
Nimmt nicht nur in der `__init__()` alles an, was man nach `int` Konvertieren lässt, sondern lässt sich auch mit solchen Objekten in Rechnungen verwenden. Zum Beispiel mit `int`\s.

Verfasst: Donnerstag 13. März 2008, 18:09
von numerix
@audax
Das ist jetzt mal Code, den auch ich verstehe ... :D

Danke, war wirklich hilfreich. Wirft bei mir natürlich gleich weitere Fragen auf:

Warum

Code: Alles auswählen

@classmethod
def fromstring(cls, value):
    return cls(int(value,2))
statt z.B.

Code: Alles auswählen

@staticmethod
def fromstring(value):
    return Dual(int(value,2))
Und: Wenn das "Python thinking" ist, okay. Macht mich aber ein bisschen traurig, weil ich es viel netter fänd, wenn Dual("1010") ebenso möglich wäre wie Dual(15).

@BlackJack
Danke auch für den Entwurf. Den mittleren Teil - Zeile 26-37 - verstehe ich im Moment noch nicht, aber das ist vorerst nicht weiter schlimm; bei nächster Gelegenheit arbeite ich mich dadurch.

Jetzt bin ich natürlich völlig verwirrt: audax will eine extra Methode zur Erzeugung von Dual-Objekten aus einem "dualen String", du machst es nun an der Stelle so ähnlich wie ich in meinem ersten Entwurf und verwendest auch isinstance() :?:

Was mich immer noch nicht glücklich macht:
Bei audax' Variante führt die Erzeugung eines Dual-Objekts aus einem float zunächst nicht zu einem Fehler; erst beim Rechnen oder Ausgeben kommt die Fehlermeldung.
Bei BlackJacks Variante wird wiederum der float kommentarlos zu einem int und dann weiter verarbeitet.

Richtig toll ist natürlich, dass man mit BlackJacks Version jetzt auch int's mit Dual's verrechnen kann. Das gefällt mir.

Verfasst: Donnerstag 13. März 2008, 18:10
von Zap
pütone hat geschrieben:

Code: Alles auswählen

>>> from bin import *
>>> bin(1)
'10'
>>> bin(1,True)
'10'
>>> bin(-10)
'10100'
>>> bin(-10,True)
'01101'
:oops: Hatte nen "dreher" in den Rückgabewerten, habs oben geändert.

Verfasst: Donnerstag 13. März 2008, 18:47
von BlackJack
@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.

Die Zeilen 26-37 erzeugen die ganzen Rechenmethoden in der Liste programmatisch. Hätte man auch mit "Copy'n'Paste"-Programmierung machen können.

Ich behandle Zeichenketten in der `__init__()` besonders weil das IMHO nicht ganz unpythonisch ist. Man sieht das jedenfalls häufiger. Das ist halt ein "besonderer" Datentyp.

Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)

Verfasst: Donnerstag 13. März 2008, 19:09
von numerix
BlackJack hat geschrieben:@pütone: Wenn man von audax' Klasse eine neue ableitet und darauf `fromstring()` aufruft, wird eine Instanz dieser neuen Klasse zurückgegeben und nicht wie bei Deiner `staticmethod` ein Objekt vom Typ `Dual`.
Dachte, ich hätte es verstanden. Wollte es ausprobieren. Und zwar so:

Code: Alles auswählen

>>> from dual import *
>>> class MyDual(Dual):
...     pass
...
>>> MyDual.classmeth('10101')
<Dual 10101-21>
>>> MyDual.staticmeth('10101')
<Dual 10101-21>
Dann habe ich es doch nicht verstanden.
BlackJack hat geschrieben:Dass man auch `float`\s mit Dual verrechnen kann, gefällt Dir wahrscheinlich weniger, oder? :-)
Grausam :x

Verfasst: Donnerstag 13. März 2008, 20:34
von BlackJack
@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)

Verfasst: Donnerstag 13. März 2008, 20:51
von numerix
BlackJack hat geschrieben:@pütone: Du bist auf ein Exemplar vom Typ `MyDual` reingefallen das "lügt". Was Du da siehst ist die Zeichenkette, die `__repr__()` zurück gibt, und nicht der Typ des Objekts. Teste mal mit ``type(MyDual.classmeth('10101'))`` und Du findest Erleuchtung. :-)
Jaaaah! :idea: :idea: :idea:

Verfasst: Donnerstag 13. März 2008, 22:25
von audax
@Blackjack:
Exakt die Lösung ist mir vorhin auch eingefallen :D
Brauchte sowas, um dummy-Klassen für Tests zu generieren. Python ist schon recht flexibel :]

Und...basestring kannte ich noch nicht. Sehr pratkisch!

Verfasst: Donnerstag 13. März 2008, 22:52
von BlackJack
@rabit: Um noch eine konkrete Antwort auf die Frage nach der Binärdarstellung von 2.35 zu liefern: 10.010110₂, wobei der unterstrichene Teil die Periode ist. Der Dezimalbruch lässt sich binär nicht mit endlichen Stellen darstellen.