Dezimal mit Punkt in String mit Komma umwandeln

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Hallo,

mein Code ist:

Code: Alles auswählen

from contextlib import closing

from mysql.connector import connect


def get_data_for_grid(host, port, user, password, database, sql):
    with connect(
        host=host, port=port, user=user, password=password, database=database
    ) as connection:
        with connection.cursor() as cursor:
            cursor.execute(sql)
            return [
                tuple((str(item) if item is not None else "") for item in row)
                for row in cursor.fetchall()
            ]
Wie kann ich die Zeile:

Code: Alles auswählen

tuple((str(item) if item is not None else "") for item in row)
so ändern/erweitern das im Falle das item eine decimal-Zahl ist der Punkt durch ein Komma ersetzt wird. Diese Umwandlung darf nicht erfolgen wenn item keine decimal-Zahl ist (z.B. also ein String der einen Punkt enthält)

Wie könnte man das erledigen?


zusätzlich:
wie kann man diese Umwandlung noch erweitern um mögliche Tausendertrenner richtig umzuwandeln?

z.B. 1,234,567.89 soll zu "1.234.567,89" werden


Gruß
Stephan
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Indem du str durch eine Funktion ersetzt, die dein gewünschtes Verhalten produziert. Und für die deutsche Formatierung würde ich das hier einsetzen: http://babel.pocoo.org/en/latest/api/numbers.html
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei ich da nicht nur `str()` ersetzen würde, sondern den gesamten bedingten Ausdruck, denn es soll ja nicht nur `float()` (und `decimal.Decimal`) besonders behandelt werden, sondern auch `None`. Das könnte (sollte IMHO) dann auch in der eigenen Umwandlungsfunktion verschwinden. Und bevor man da dann selbst anfängt auf Typen zu prüfen, würde ich einen Blick auf `functools.singledispatch()` werfen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Indem du str durch eine Funktion ersetzt, die dein gewünschtes Verhalten produziert.
Richtig. Und weil ich diese Funktion nicht selbst hinbekomme, habe ich den Thread hier eröffnet. Insbesondere sollen ja nicht alle "item" umgewandelt werden, sondern nur Bestimmte.

Pseudocode wäre also:

WENN "item" ungleich NULL Dann:
WENN "item" decimal ist dann format_decimal(item, locale='de')
Sonst: "item" = ""

Wie aber schreibe ich das in Python?


Gruß
Stephan
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und ich habe dir Hinweise gegeben, mit welchen Mitteln du zu diesem Ergebnis kommst. Die Grundlagen von Funktionen und wenn-dann-Entscheidungen musst du schon selbst mitbringen. Sie kommen ja auch in deinem Code schon vor. Warum stellen die plötzlich eine Hürde dar?

Was zum Glück vielleicht noch fehlt: typprüfung kann man mit isinstance(wert, typ) machen. __blackjack__ hat aber auch schon eine Alternative benannt.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Warum stellen die plötzlich eine Hürde dar?


offen gesagt:
WEIL mir, hier im Forum, immer wieder mein eigener Code ausgeredet wird und mir anderer Code nahegelegt wird. Was ich damit meine ist das ich als Python-Anfänger zweifelsohne keinen guten Code schreibe, aber solange der Code funktioniert, sollte das doch aktzeptiert werden, aber nein, mein Code wird als unzulänglich abgetan und ich darf dann wählen ob ich notgedrungen fertigen Code nehme der mir gepostet wird oder nichts, denn wirklich eingegangen auf Nachfragen wird nur sehr begrenzt.

Deshalb ist auch der hier von mir gepostete Code nicht selbst geschrieben und ich verstehe ihn nicht in jeder Einzelheit.


Gruß
Stephan
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe mir deine Beiträge angesehen, und dir ist hier viel und kompetent geholfen worden. Sich aus berechtigter Kritik an deinem Code ein Recht auf fertige Lösungen abzuleiten ist - genauso offen gesagt - anmaßend.
Buchfink
User
Beiträge: 193
Registriert: Samstag 11. September 2021, 10:16

@Stephan_2021

ich möchte mir erlauben folgendes zu sagen:
Ich persönlich finde es als Python Novize eigentlich immer ziemlich prima, wenn ich hier im Forum gleich ein "kostenloses" Code-Review mitgeliefert bekomme. Denn da muss man den Leuten, die hier helfen imho dankbar sein, dass sie sich sogar die Zeit nehmen, den Code zu optimieren.

Wenn du den gelieferten Code nicht gleich verstehst, wäre es doch vielleicht auch ein Ansatzpunkt sich mal anzusehen, was dahinter steckt.

Ich kann verstehen, dass Kritik manchmal schwer hinzunehmen ist. Aber meine Erfahrung als Entwickler ist, dass diese Art der Interaktion - nämlich "CodeReview" einem in 99% der Fälle viel mehr bringt, als vieles andere.

LG
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mit `singledispatch()` + Testcode für die Umwandlungsfunktion:

Code: Alles auswählen

#!/usr/bin/env python3
from contextlib import closing
from decimal import Decimal
from functools import singledispatch

import pytest
from mysql.connector import connect


@singledispatch
def convert_to_string(value):
    return str(value)


@convert_to_string.register(type(None))
def _convert_none(_value):
    return ""


@convert_to_string.register(float)
@convert_to_string.register(Decimal)
def _convert_number(value):
    return format(value, ".2f").replace(".", ",")


def get_data_for_grid(host, port, user, password, database, sql):
    with closing(
        connect(
            host=host,
            port=port,
            user=user,
            password=password,
            database=database,
        )
    ) as connection:
        with closing(connection.cursor()) as cursor:
            cursor.execute(sql)
            return [
                tuple(map(convert_to_string, row)) for row in cursor.fetchall()
            ]


@pytest.mark.parametrize(
    "value, expected",
    [
        ("test", "test"),
        (None, ""),
        (42, "42"),
        (2.3, "2,30"),
        (Decimal("4.2"), "4,20"),
    ],
)
def test_convert_to_string(value, expected):
    assert convert_to_string(value) == expected
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da fehlen aber noch die 1000er-Punkte. Darum würde ich da Babel nehmen.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@__deets__,

kann es bei einen Datenbankabfrage überhaupt verschiedenen Zahlenformate geben? Als Zeichenkette wäre zwar alles möglich, aber wieso sollten Dezimalzahlen als Zeichenketten abgelegt werden.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Wenn du den gelieferten Code nicht gleich verstehst, wäre es doch vielleicht auch ein Ansatzpunkt sich mal anzusehen, was dahinter steckt.
Genauso sehe ich das ebenfalls. Ich muss das aber quasi schrittweise tun, damit für mich die Lernkurve nicht unerklimmbar steil wird.
Ich kann verstehen, dass Kritik manchmal schwer hinzunehmen ist.
Kritik finde ich gut, denn durch Kritik kann ich mich verbessern.


Gruß
Stephan
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Mit `singledispatch()` + Testcode für die Umwandlungsfunktion:
Danke. Das muss ich jetzt erst einmal durcharbeiten.

z.B. etwas wie:

Code: Alles auswählen

with closing(connection.cursor()) as cursor:
            cursor.execute(sql)
treibt mir den Schweiß auf die Stirn, denn es ist auf den ersten Blick komplett unverständlich für mich.


Gruß
Stephan
Zuletzt geändert von Stephan_2021 am Montag 20. September 2021, 00:00, insgesamt 2-mal geändert.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Als Zeichenkette wäre zwar alles möglich, aber wieso sollten Dezimalzahlen als Zeichenketten abgelegt werden.
Falls sich diese Frage auch an mich richtet, so ist sie einfach zu beantworten: es sollen keine Zahlen als Strings abgelegt werden, sondern ich will per Starbasic in einem:
http://www.openoffice.org/api/docs/comm ... lGrid.html

Werte anzeigen und dort sind nur Strings übergebbar. Ich könnte die notwendige Umwandlung auch in Basic machen, allein ist es schneller wenn ich es gleich in Python tue. (Geschwindigkeit ist relevant, weil es um einige tausend Datensätze geht)


Gruß
Stephan
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Stephan_2021,

die Frage war eher, warum bei der Datenbankabfrage (mit fetchall), verschiedene Zahlenformate entstehen können. Dezimalzahlen dürften ja in einem entsprechenden Datentyp in der MySQL Datenbank abgelegt sein.
Daher verstehe ich nicht, wieso du bei numerischen Datentypen Tausendertrenner erwartest. Es sei denn, diese wurden schon so als Zeichenkette in der Datenbank abgelegt.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

die Frage war eher, warum bei der Datenbankabfrage (mit fetchall), verschiedene Zahlenformate entstehen können. Dezimalzahlen dürften ja in einem entsprechenden Datentyp in der MySQL Datenbank abgelegt sein.
Daher verstehe ich nicht, wieso du bei numerischen Datentypen Tausendertrenner erwartest. Es sei denn, diese wurden schon so als Zeichenkette in der Datenbank abgelegt.
ich bin Python-Anfänger und da ich mit dem Datenbankzugriff per Python irgendwo beginnen musste habe ich mit dem Datentyp "Text" begonnen und bin jetzt bei "decimal" auf das 'Problem' gestossen um das sich der Thread dreht. Mir fehlt bisher schlicht der Gesamtüberblick über alle Datentypen und wie sich Python diesbezüglich verhält und ob es Besonderheiten gibt ... was auch immer.
Ich habe keine Zeit mich systematisch einzuarbeiten, sondern mache das Stück für Stück, je nachdem wie bei mir die Notwendigkeit auftaucht bestimmte DAtentypen zu benutzen. Der Nächste wird wohl "date" sein.


GRuß
Stephan
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@Stephan_2021,

ich frage nach, damit du es dir vielleicht einfacher machen kannst. Es wäre zumindest interessant zu wissen was für Datentypen du von der Datenbank erwartest (nicht die Python-Datentypen).
Die SQL-Query wird ja wahrscheinlich eine Tabelle der Datenbank abfragen und die einzelnen Spalten werden wahrscheinlich verschiedene Datentypen haben. Wenn du schon vorher weißt welche das sind, kannst du die Umwandlung in Python möglicherweise einfacher gestallten. Die Frage ist also, was für Datentypen hat die SQL-Tabelle?
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Die Frage ist also, was für Datentypen hat die SQL-Tabelle?
derzeitig (aber das ist noch nicht zwingend vollständig, denn es wir noch etliche Tabellen mehr geben) sind das die Typen (in MySQL):

TEXT
VARCHAR
INT
DECIMAL
TINYINT
DATE


Gruß
Stephan
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der entscheidende Kontext hier ist, dass die Daten in libre office überführt werden. Und das bringt seine eigenen Herausforderungen mit sich.

Im konkreten Fall hier würde ich in frage stellen, dass eine Wandlung in Text überhaupt das richtige Vorgehen ist. Stattdessen sollten aus den decimals floats werden, damit die Fragen nach Formatierung in libre Office geklärt werden können.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Im konkreten Fall hier würde ich in frage stellen, dass eine Wandlung in Text überhaupt das richtige Vorgehen ist.
meine Antwort darauf ist: ja.
Ich hatte auch den Grund bereits erklärt, nämlich das ich die Werte per StarBasic an ein http://www.openoffice.org/api/docs/comm ... lGrid.html übergeben muss und dort nur Text möglich ist.
Stattdessen sollten aus den decimals floats werden,
mmh...
damit die Fragen nach Formatierung in libre Office geklärt werden können.
genau das will ich, der Geschwindigkeit wegen, vermeiden, denn in LO/StarBasic läuft das alles erheblich langsamer als in Python.
Genau das (die bessere Geschwindigkeit) ist der Grund mich überhaupt mit Python zu beschäftigen, weil ich mit Basic aktuell 'am Limit' bin, weil ich große Datenmengen handhaben muss.


Gruß
Stephan
Antworten