Binäreinheiten umrechnen

Code-Stücke können hier veröffentlicht werden.
Antworten
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hi!

Da rafael mir seinen Code zum korrigieren angedreht hat, dachte ich mir, dass ich ihn gleich mal hierher poste, damit mehrere davon was haben.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

from __future__ import division
from sys import argv

# <name> <arbitary amont of names> <how many bit long>
formats = (
    ('Bit', 2**0),
    ('Nibble', 2**2),
    ('Byte', 2**3),
    ('Kibibyte', 'KB', 'KiB', 2**13),
    ('Mebibyte', 'MiB', 2**23),
    ('Gibibyte', 'GiB', 2**33),
    ('Tebibyte', 'TiB', 2**43),
    ('Pebibyte', 'PiB', 2**53),
    ('Exbibyte', 'EiB', 2**63),
    ('Zebibyte', 'ZiB', 2**73),
    ('Yobibyte', 'YiB', 2**83)
)

def process_formats_for(formats):
    """Generate a nice dict containing the names and bit sizes"""
    format_bytes = dict()
    for line in formats:
        names = line[:-1]
        num_bytes = line[-1]
        for name in names:
            format_bytes[name] = num_bytes
    return format_bytes

def process_formats_expression(formats):
    """another, compatible version (taken straight from hell) specially
    made for Y0Gi. Thanks to birkenfeld for explaining me the syntax
    of a LC which iterates through two values.
    explanation: first splitting the format tuple in names and values
    then wrapped in a LC which iterates through the names and creates
    new tuples which contain only the pair (name, value).
    finaly fed that into dict(), to create a dictionary"""
    return dict(
        (name, single_format[1]) for single_format in
            # split the format in name tuple and bit count
            ((format[:-1], format[-1]) for format in formats)
        for name in single_format[0])

# choose format processor
# choices are uninteresting: for or insane: expression
process_formats = process_formats_expression
format_bytes = process_formats(formats)

def format(value, fmt, new_fmt):
    old_size, new_size = format_bytes[fmt], format_bytes[new_fmt]
    ratio = old_size / new_size
    output = value * ratio
    return (output, new_fmt, fmt)

def main():
    value = float(argv[1])
    output, new_fmt, fmt = format(value, *argv[2:4])
    print "%g %s are %g %s" % (value, fmt, output, new_fmt)

if __name__ == '__main__':
    main()
Edit: Einige von Y0Gis Vorschlägen übernomen.
Edit: Einige von rafaels Anmerkungen übernommen
Edit: BlackJacks %g-Tipp übernommen.
Edit: Generation des Dicts durch LCs erledigt.
Edit: Y0Gis Vorschlag GEs zu verwenden integriert, Formatleser nun konfigurierbar
Zuletzt geändert von Leonidas am Freitag 31. August 2007, 18:46, insgesamt 8-mal geändert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Welchen Teil von "Use spaces around arithmetic operators." hast du nicht verstanden? ;P

Edit:
Und wo wir grad mal bei Spitzfindigkeit sind (das hast du jetzt davon): `input` ist natürlich etwas ungünstig gewählt (und das Syntax-Highlighting dieses Forums macht es einem noch sehr deutlich - was Anfänger möglichweise auch noch verwirrt). `value` ist mein Vorschlag.

Dies ist auch eine der Gelegenheiten, wo man `from sys import argv` beispielhaft als `from`-Import verwenden *kann* um die Lesbarkeit - in diesem Rahmen - *etwas* zu erhöhen. (Bei übermäßiger Nutzung, d.h. bei Imports aus vielen Modulen, kann das aber auch schnell unübersichtlich werden.)

Der Code zum Erstellen des Dictionaries gefällt mir noch nicht so ganz. Dass du den Dingen Namen gibst, ist von Vorteil. Letztlich aber schreit irgendwas in mir danach, daraus einen (hoffentlich noch einigermaßen nachvollziehbaren), umgebrochenen Einzeiler zu machen; besonders das leer erzeugte Dictionary. Allerdings finde ich die Verwendung von mehr als einem `for` in einer LC/GE ungünstig bis verwirrend - zumal da IIRC auch die Reihenfolge umgekehrt zur (von mir subjektiv vermuteten) Leserichtung zu sein hat. Da geht's schon los mit meiner Unsicherheit ;)

Dennoch: Gut gemacht :)
querdenker
User
Beiträge: 424
Registriert: Montag 28. Juli 2003, 16:19
Wohnort: /dev/reality

Leonidas hat geschrieben: ...
Da rafael mir seinen Code zum korrigieren angedreht hat, ...
Du läßt dir wohl alles andrehen :lol:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Huhu, jetzt bekomme ich auch mal Kritik für meine Programme - war ja schon lang nicht mehr der Fall ;)
Y0Gi hat geschrieben:Welchen Teil von "Use spaces around arithmetic operators." hast du nicht verstanden? ;P
Hu, bis auf die ``2**X`` habe ich das überall gemacht? Und ``2 ** X`` mag ich persönlich nicht, finde ich unübersichtlicher. Ein Exponent ist in meinem Gehirn recht fest mit seiner Basis verdrahtet. Klar, es ist nicht 100% konsistent, aber bewusst so geschrieben. Oder wie sagt PEP8: "A Foolish Consistency is the Hobgoblin of Little Minds" :D
Y0Gi hat geschrieben:Und wo wir grad mal bei Spitzfindigkeit sind (das hast du jetzt davon): `input` ist natürlich etwas ungünstig gewählt (und das Syntax-Highlighting dieses Forums macht es einem noch sehr deutlich - was Anfänger möglichweise auch noch verwirrt). `value` ist mein Vorschlag.
Richtig, ``input`` habe ich aus rafaels Code übernommen und dann ist mir kein anderer Name auf die schnelle eingefallen. Und um ``input`` wars mir dann doch nicht schade. ``value`` ist nett, das werde ich gleich mal übernehmen.
Y0Gi hat geschrieben:Dies ist auch eine der Gelegenheiten, wo man `from sys import argv` beispielhaft als `from`-Import verwenden *kann* um die Lesbarkeit - in diesem Rahmen - *etwas* zu erhöhen. (Bei übermäßiger Nutzung, d.h. bei Imports aus vielen Modulen, kann das aber auch schnell unübersichtlich werden.)
Ack, good point. Habe ich übernommen. Ist tatsächlich lesbarer.
Y0Gi hat geschrieben:Der Code zum Erstellen des Dictionaries gefällt mir noch nicht so ganz. Dass du den Dingen Namen gibst, ist von Vorteil. Letztlich aber schreit irgendwas in mir danach, daraus einen (hoffentlich noch einigermaßen nachvollziehbaren), umgebrochenen Einzeiler zu machen; besonders das leer erzeugte Dictionary. Allerdings finde ich die Verwendung von mehr als einem `for` in einer LC/GE ungünstig bis verwirrend - zumal da IIRC auch die Reihenfolge umgekehrt zur (von mir subjektiv vermuteten) Leserichtung zu sein hat.
Richtig, da wäre noch optimierungsbedarf, mal sehen ob sich da was machen lässt. Dict Comprehensions (PEP 274) wurden aber zumindest für Python 2.x zurückgezogen. Vielleicht lässt sich das mit etwas ``dict()`` Magie hinbekommen, da werd ich doch gleich mal schauen :D

Was mir noch fehlt ist ein Modifier für ``%f`` der die überzähligen Nullen am Ende wegschneidet.

Ich glaube ich stelle mich hier etwas dämlich an, aber beim Verbessern bin ich darauf gestoßen:

Code: Alles auswählen

In [239]: fmt = [(f[:-1], f[-1]) for f in formats]
In [240]: fmt
Out[240]: [(('Bit', 'Bit'), 1), (('Nibble', 'Nibble'), 4), (('Byte', 'Byte'), 8), (('Kibibyte', 'KB', 'KiB'), 8192), (('Mebibyte', 'MiB'), 8388608), (('Gibibyte', 'GiB'), 8589934592L), (('Tebibyte', 'TiB'), 8796093022208L), (('Pebibyte', 'PiB'), 9007199254740992L), (('Exbibyte', 'EiB'), 9223372036854775808L), (('Zebibyte', 'ZiB'), 9444732965739290427392L), (('Yobibyte', 'YiB'), 9671406556917033397649408L)]
In [241]: [name for name in f[0] for f in fmt]
Out[241]: ['Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB']
Wie kann das sein? Wenn ich es ausschreibe gehts normal:

Code: Alles auswählen

for f in fmt:
    for name in f[0]
        print name
Zuletzt geändert von Leonidas am Freitag 31. August 2007, 12:56, insgesamt 2-mal geändert.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
rafael
User
Beiträge: 189
Registriert: Mittwoch 26. Juli 2006, 16:13

Ah, hab meinen Fehler gefunden. Danke! :D

Jedoch musst du Zeile 38 anpassen (``sys.argv`` -> ``argv``).

Und ``from __future__ import division`` muss noch oben hin.
Zuletzt geändert von rafael am Freitag 31. August 2007, 13:02, insgesamt 1-mal geändert.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Kibibytes mit KB zu bezeichnen ist reichlich irreführend.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

mkallas hat geschrieben:Kibibytes mit KB zu bezeichnen ist reichlich irreführend.
Habe ich 1:1 aus der Wikipedia übernommen:
Wikipedia hat geschrieben:Kibibyte (KiB) (210 Byte = 1024 Byte), üblicherweise wird jedoch KB zur Unterscheidung von kB geschrieben, weil es geläufiger ist.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

Wegen der überflüssigen 0en am Ende könnte man mal '%g' statt '%f' verwenden.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Leonidas hat geschrieben: Ich glaube ich stelle mich hier etwas dämlich an, aber beim Verbessern bin ich darauf gestoßen:

Code: Alles auswählen

In [239]: fmt = [(f[:-1], f[-1]) for f in formats]
In [240]: fmt
Out[240]: [(('Bit', 'Bit'), 1), (('Nibble', 'Nibble'), 4), (('Byte', 'Byte'), 8), (('Kibibyte', 'KB', 'KiB'), 8192), (('Mebibyte', 'MiB'), 8388608), (('Gibibyte', 'GiB'), 8589934592L), (('Tebibyte', 'TiB'), 8796093022208L), (('Pebibyte', 'PiB'), 9007199254740992L), (('Exbibyte', 'EiB'), 9223372036854775808L), (('Zebibyte', 'ZiB'), 9444732965739290427392L), (('Yobibyte', 'YiB'), 9671406556917033397649408L)]
In [241]: [name for name in f[0] for f in fmt]
Out[241]: ['Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'Yobibyte', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB', 'YiB']
Wie kann das sein? Wenn ich es ausschreibe gehts normal:

Code: Alles auswählen

for f in fmt:
    for name in f[0]
        print name
Geh mal in einen "sauberen" Namespace:

Code: Alles auswählen

fmt = [(('Bit', 'Bit'), 1), (('Nibble', 'Nibble'), 4), (('Byte', 'Byte'), 8), (('Kibibyte', 'KB', 'KiB'), 8192), (('Mebibyte', 'MiB'), 8388608), (('Gibibyte', 'GiB'), 8589934592L), (('Tebibyte', 'TiB'), 8796093022208L), (('Pebibyte', 'PiB'), 9007199254740992L), (('Exbibyte', 'EiB'), 9223372036854775808L), (('Zebibyte', 'ZiB'), 9444732965739290427392L), (('Yobibyte', 'YiB'), 9671406556917033397649408L)]
>>> [name for name in f[0] for f in fmt]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'f' is not defined
>>> [name for f in fmt for name in f[0]]
['Bit', 'Bit', 'Nibble', 'Nibble', 'Byte', 'Byte', 'Kibibyte', 'KB', 'KiB', 'Mebibyte', 'MiB', 'Gibibyte', 'GiB', 'Tebibyte', 'TiB', 'Pebibyte', 'PiB', 'Exbibyte', 'EiB', 'Zebibyte', 'ZiB', 'Yobibyte', 'YiB']
MaW: Die Reihenfolge der "for"-Clauses ist verkehrt.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Danke birkenfeld, das wars. Die Syntax ist gewöhnungsbedürftig, aber wenn mans weiß, dann gehts schon. Und wer konstruiert denn schon solche LCs?

Y0Gi, du hast es nicht anders gewohnt, ich habe den Code aktualisiert und neben einigen Bugfixes auch einen "Einzeiler" geschrieben, der die Datenstruktur in ein Dict überführt. Habe es aber dann doch umgebrochen, weil es ziemlich grausam ist.

Für alle die sich wundern wie man solche LCs konstruiert: die sind in Teilen entstanden: erst die innere LC so weit angepasst, unter einem temporären Namen abgespeichert, äußere LC geschrieben bis sie richtig funktioniert hat und dann die eine LC in die andere eingesetzt (und ggf. zu Generator Expressions gemacht).

Aber ich finde den Code total grausam, daher habe ich noch einen Beschreibungstext dazu geschrieben, damit man die Chance hat, den zu verstehen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

Hehe :)

In dem Horror-Konstrukt sollte das äußerste Klammernpaar innerhalb von `dict()` wegfallen können, da Klammern bei GE nur erforderlich sind, wenn sie nicht bereits von solchen umschlossen werden (wie eben als Argument für eine Callable). Und warum verwendest du in der Mitte eine LC und nicht auch eine GE? Eine Liste brauchst du ja nicht zwingend?

Ich überlege auch, wie man die Ausgangsdatenstruktur für die Weiterverwendung optimieren kann. So könnte man z.B. alle Einheitsbezeichner in ein eigenes Tupel auslagern, so dass `formats` nur 2-Tupel aus Bezeichnern und der Zweierpotenz enthält.

Man könnte auch ein Dictionary verwenden und die Zweierpotenzen als Keys verwenden - immutable sind die ja. Die Bezeichnertupel werden dann die jeweiligen Werte. So sind die Zweierpotenzen auch nicht redundant vorhanden. Entsprechend müsste aber der Lookup an dieses neuen Format-Dictionary angepasst werden und ist ggf. nicht so performant und elegant.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Y0Gi hat geschrieben:In dem Horror-Konstrukt sollte das äußerste Klammernpaar innerhalb von `dict()` wegfallen können, da Klammern bei GE nur erforderlich sind, wenn sie nicht bereits von solchen umschlossen werden (wie eben als Argument für eine Callable). Und warum verwendest du in der Mitte eine LC und nicht auch eine GE? Eine Liste brauchst du ja nicht zwingend?
Dir entgeht aber auch nichts :D Waren wieder so ich-bin-zu-faul-Fälle. Einen richtigen Unterschied macht es aber wohl kaum, da die innere GE normalerweise sowieso nicht viele Werte verarbeiten muss. Vielleicht wollte ich unterbewusst auch die Ähnlichkeit zu LISP ausschließen? ;)
Y0Gi hat geschrieben:Ich überlege auch, wie man die Ausgangsdatenstruktur für die Weiterverwendung optimieren kann. So könnte man z.B. alle Einheitsbezeichner in ein eigenes Tupel auslagern, so dass `formats` nur 2-Tupel aus Bezeichnern und der Zweierpotenz enthält.
Ich habe mir überlegt, die ursprüngliche Datenstruktur zu ändern. Aber wenn man etwas ändern oder etwas neues eingeben will, dann ist diese Struktur ziemlich optimal. Wenn man eine Struktur nimmt, die nur 2-Tupel enthält, dann ist es natürlich einfacher zu parsen, denn sowas kann ``dict()`` direkt verarbeiten (das ist ja eben genau das was die GE in ``process_format_expression`` letztendlich macht). Aber dann sind die Eingabedaten redundant und wenn der User das eingibt dann kotzt ihn das an oder er macht Fehler (nicht das ein User da jemals rumeditieren würde - wozu auch, aber wir diskutieren hier Grundlegende Konzepte, denke ich).

Dann kann man auch direkt ein Dictionary als Eingabeformat nehmen, welches dem Namen eine Anzahl Bytes zuweist und spart sich sie Transformation völlig. Auch ein durchaus gängiger Weg.
Y0Gi hat geschrieben:Man könnte auch ein Dictionary verwenden und die Zweierpotenzen als Keys verwenden - immutable sind die ja. Die Bezeichnertupel werden dann die jeweiligen Werte. So sind die Zweierpotenzen auch nicht redundant vorhanden. Entsprechend müsste aber der Lookup an dieses neuen Format-Dictionary angepasst werden und ist ggf. nicht so performant und elegant.
Ja, das ist dann etwas speichersparender aber wenn das nicht die Eingabedatenstruktur ist, dann wird es direkt noch schwerer, sowas in einer GE zu transformieren ;)

Ich denke, dass die aktuelle Datenstruktur trotz dessen, dass die Values mehrfach gespeichert sind ziemlich gut ist, so wie sie ist. Man kann sie leicht Abfragen, was bei anderen Strukturen mehr Aufwand bedeuten würde.

Das ist eben so ein Fall von: wie strukturiere ich meine Daten am besten.

Oha, dieser Quelltext hat ja eine Diskussion erster Klasse ins Leben gerufen 8)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten