2 tuple (?) in ein tuple zusammenfassen?

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

Hallo,

vorweg: ich bin Anfänger und entschuldige mich im Voraus, wenn ich wieder falsche Begriffe verwenden sollte. In meiner Begriffswelt als Basic-Programmierer geht es im Folgenden um Arrays, aber mir ist hier im Forum schon in aller DEutlichkeit gesagt worden das es das in Python nicht gibt, sondern nur tuple und listen und worum es im Folgenden geht sind dann wohl tuple.

ich habe folgenden Code:

Code: Alles auswählen

import mysql.connector as mc
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()
            ]
            
def get_columnnames(host, port, user, password, database, sql):
    connection = mc.connect(host = host,
        port = port,
        user = user,
        password = password,
        database = database)

    cursor = connection.cursor()
    cursor.execute(sql)

    resultraw = cursor.fetchall()
    columns = cursor.column_names
    cursor.close()
    connection.close()
    
    return columns
Beide Funktionen (get_data_for_grid und get_columnnames) kann ich aus LibreOffice mittels Starbasic aufrufen und sie liefern jeweils (aus Sicht von Starbasic) ein Array von Werten. Alle so wie ich es will.

Ich möchte nun beide Python Funktionben in Einer verbinden so das deren Rückgabe die Kombination beider Rückgaben ist. Derzeig habe ich:

Code: Alles auswählen

def kombiniert(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)
            ds = [
                tuple((str(item) if item is not None else "") for item in row)
                for row in cursor.fetchall()
            ]
            
            columns = cursor.column_names
            
            return [columns, ds]
das ist aber nicht das Richtige, denn die quasi Struktur der Rückgabe ist (sofern columns entspricht ["a","b","c"] und ds entspricht [1,2,3]) :

[["a","b","c"],[[4,5,6]]]

ich brauche aber:

[["a","b","c"],[4,5,6]]


Könnt ihr verstehen was ich meine?



Gruß
Stephan
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Bist du dir sicher, dass das dir Rückgabe der Funktion ist? Ich denke, dass dem nicht so ist.

ds (steht wahrscheinlich für Dachsanierung. Vielleicht auch für "Datensatz". Wissen kann man das nicht. Aber es ist kein Datensatzn, denn es) ist eine Liste, die je row einen Tuple enthält, der wieder die Werte der Felder enthält.
Wenn du nicht möchtest, dass ds eine Liste ist, welche row würdest du denn dann zurück liefern wollen?
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Bist du dir sicher, dass das dir Rückgabe der Funktion ist?
naja, in jedem Falle bin ich mir sicher WIE die Rückgabe der Funktion ist, dennn ich habe es ausprobiert. Vielleicht habe ich es hier im Forum falsch skizziert?
Aber es ist kein Datensatzn, denn es) ist eine Liste, die je row einen Tuple enthält, der wieder die Werte der Felder enthält.
Wenn du nicht möchtest, dass ds eine Liste ist, welche row würdest du denn dann zurück liefern wollen?
Tja, kann sehr gut sein das ich liste und tuple weiterhin ungenügend verstehe. Ich meine das es richtig wäre zu sagen das eine Liste eine 'Auflistung' von Werten gleichen Typs ist und ein tuple eine 'Auflistung' von Werten von ggf. (nicht zwingend) unterschiedlichen Typs.

Vielleicht mache ich es mal konkret für die bereits genannten Funktionen und meine Datenbank was ich erreichen will. Mittels Starbasic rufe ich derzeitig z.B. auf:

Code: Alles auswählen

oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory")
g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("")
oScript = g_MasterScriptProvider.getScript("vnd.sun.star.script:mysqlgetdata2.py$get_data_for_grid?language=Python&location=user")
txtSQL = "SELECT * from `dolmetscher-uebersetzer` WHERE `DolmID` = " & .Model.GridDataModel.getCellData(13, .getCurrentRow())
RS_Python = oScript.invoke(Array(DBHost, DBPort, DBUser, DBPass, NameDBInMySQL, txtSQL), Array(), Array())
					
Msgbox RS_Python(0) (7)

das liefert z.B.:

Lindenstr. 78

und ich rufe auf:

Code: Alles auswählen

'...
oScript = g_MasterScriptProvider.getScript("vnd.sun.star.script:mysqlgetdata2.py$get_columnnames?language=Python&location=user")
txtSQL = "SELECT * from `dolmetscher-uebersetzer` WHERE `DolmID` = " & .Model.GridDataModel.getCellData(13, .getCurrentRow())
RS_Python = oScript.invoke(Array(DBHost, DBPort, DBUser, DBPass, NameDBInMySQL, txtSQL), Array(), Array())
					
Msgbox RS_Python(7)
und das liefert:

Strasse


ich hätte nun gerne die 2 Pythonfunktionen, so kombiniert das deren Rückgabe in Basic so abfragbar wäre:

Code: Alles auswählen

'...
oScript = g_MasterScriptProvider.getScript("vnd.sun.star.script:mysqlgetdata2.py$kombiniert?language=Python&location=user")
txtSQL = "SELECT * from `dolmetscher-uebersetzer` WHERE `DolmID` = " & .Model.GridDataModel.getCellData(13, .getCurrentRow())
RS_Python = oScript.invoke(Array(DBHost, DBPort, DBUser, DBPass, NameDBInMySQL, txtSQL), Array(), Array())
					
Msgbox RS_Python(0) (7) & CHR(13) & RS_Python(1) (7)
soll liefern:

Strasse
Lindenstr. 78




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

Nachtrag:

Stephan_2021 hat geschrieben: Sonntag 19. September 2021, 15:27
ich hätte nun gerne die 2 Pythonfunktionen, so kombiniert das deren Rückgabe in Basic so abfragbar wäre:

Code: Alles auswählen

'...
oScript = g_MasterScriptProvider.getScript("vnd.sun.star.script:mysqlgetdata2.py$kombiniert?language=Python&location=user")
txtSQL = "SELECT * from `dolmetscher-uebersetzer` WHERE `DolmID` = " & .Model.GridDataModel.getCellData(13, .getCurrentRow())
RS_Python = oScript.invoke(Array(DBHost, DBPort, DBUser, DBPass, NameDBInMySQL, txtSQL), Array(), Array())
					
Msgbox RS_Python(0) (7) & CHR(13) & RS_Python(1) (7)
soll liefern:

Strasse
Lindenstr. 78

mit meiner derzeitigen Funtion "kombiniert" muss ich das ERgebnis hingegen so abfragen:

Code: Alles auswählen

'...
Msgbox RS_Python(0) (7) & CHR(13) & RS_Python(1) (0) (7)
um die Rückgabe:

Strasse
Lindenstr. 78


zu erhalten.


Gruß
Stephan
Benutzeravatar
sparrow
User
Beiträge: 4183
Registriert: Freitag 17. April 2009, 10:28

Dein Code an dieser Stelle

Code: Alles auswählen

            return [
                tuple((str(item) if item is not None else "") for item in row)
                for row in cursor.fetchall()
            ]
erzeugt, wie ich bereits geschrieben habe, eine Liste von Tupeln, die die Werte der Zeilen enthalten.

Wenn du nur eine Zeile abrufst - und du sicher bist, dass es nur eine Zeile ist, dann verwendet man nicht "fetchall" sondern "fetchone". Oder du holst dir nur den ersten Eintrag aus dem Result und gibst das zurück.
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

erzeugt, wie ich bereits geschrieben habe, eine Liste von Tupeln, die die Werte der Zeilen enthalten.
ja, genau.

Wenn du nur eine Zeile abrufst - und du sicher bist, dass es nur eine Zeile ist, dann verwendet man nicht "fetchall" sondern "fetchone". Oder du holst dir nur den ersten Eintrag aus dem Result und gibst das zurück.
Ja, in Ordnung, aber zumindest die Funktion get_data_for_grid dient dem Abrufen einer beliebigen Anzahl von Zeilen.
Die Funktion get_columnnames hingegen natürlich nur den Spaltennamen (und keiner Datenzeile), so das ich nun annehme dass hier "fetchone" genügt.



Meine ursprüngliche Frage bleibt bestehen: wie kombiniere ich beide Funktionen, so das ich die gewünschte Rückgabe erhalte?



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

Stephan_2021 hat geschrieben: Sonntag 19. September 2021, 23:39
erzeugt, wie ich bereits geschrieben habe, eine Liste von Tupeln, die die Werte der Zeilen enthalten.
ja, genau.
vielleicht noch ein Nachtrag:

FALLS ich irgendeinen Begriff vertauscht habe, dann sieh mir das bitte nach, denn ich bin als Anfänger unsicher bei tuple vs. Liste. Wenn ich mich jetzt aber an Deine Formulierung halte, ist das was ich inhaltlich will das quasi der erste Tupel der Liste die Spaltennamen enthält und die weiteren Tupel jeweils einen Datensatz der Tabelle - SO soll die Struktur des Ergebnisses sein das die Funktion mittels return zurückgibt.


Gruß
Stephan
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Stephan_2021: Du hast doch bereits so eine Funktion die beides zusammenfasst: `kombiniert()`. Ich würde da auf Python-Seite keine Liste sondern ein Tupel zurückgeben, aber das wird ja auf StarBASIC-Seite dann genau so zu einem Array wie die Liste. Also mit der `convert_to_string()`-Lösung aus dem anderen Beitrag:

Code: Alles auswählen

def kombiniert(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 (
                cursor.column_names,
                [
                    tuple(map(convert_to_string, row))
                    for row in cursor.fetchall()
                ],
            )
Und ja, da ist eine Ebene Indexzugriffe ”zu viel” wenn man nur einen Datensatz abfragt, aber Du schreibst ja es soll auch die Möglichkeit geben Abfragen zu machen die mehr als einen Datensatz liefern. Und was man eher nicht will, sind APIs bei denen sich in solchen Fällen die Rückgabewerte in der Struktur unterscheiden, denn dann kommt man in Teufels Küche wenn man eine Abfrage absetzt bei der mehrere Datensätze möglich wären, konkret aber nur einer vorhanden ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

'mein Gott im Himmel'... Python sollte nicht in der Lage sein das zu können was Basic kann?

ein Beispiel in Basic ist leicht zu geben:

2 Arrays 'unterschiedlicher Ebene' und unterschiedlicher Herkunft:

Code: Alles auswählen

arr1 = Array(Array("a","b"), Array("c","d"))
x = "x-y"
arr2 = Split(x, "-")
obwohl beide Arrays eigentlich nichts miteinander zu tun haben, kann ich sie doch aber zusammenfassen (klar, habe ich selbst dabei die Verantwortung darauf zu achten das die Struktur passt), z.B. so:

Code: Alles auswählen

Dim arr3(2, 1)

For i = 0 To 1
	arr3(0, i) = arr2(i)
Next i

For i = 1 to 2
	For j = 0 to 1
		arr3(i, j) = arr1(i-1) (j)
	Next j
Next i

Und etwas Äquivalentes soll in Python nicht gehen? Ich glaubs nicht.
Und ja, da ist eine Ebene Indexzugriffe ”zu viel” wenn man nur einen Datensatz abfragt,
Nein, da ist nichts zu viel, ich hätte gerne nur beide Rückgaben (Columns und Datensätze) auf quasi der gleichen Ebene. Es muss doch möglich sein das primäre Ergebnis so umzuformen, auch wenn (und DAS bestreite ich ja garnicht) beide Dinge nicht direkt Dasselbe sind.
Das es keine Unterschiede in der Struktur geben wird ist garantiert, denn alle Tupel (ich hoffe ich verwende den Begriff hier richtig) haben zwingend gleich viele Elemente und alle Elemente sind Strings, weil das meine Absicht ist.



Gruß
Stephan
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Stephan_2021: Natürlich gibt es da was äquivalentes in Python. Man braucht da auch weniger Code weil man keine Schleifen schreiben muss und auch `arr3` nicht erst leer mit Dummywerten erstellen muss.

Code: Alles auswählen

#!/usr/bin/env python3

def main():
    iterable = [["a", "b"], ["c", "d"]]
    text = "x-y"

    result = [text.split("-"), *iterable]
    print(result)


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

Leider funktioniert Dein Code nicht wenn ich ihn, wie gewünscht, mittels StarBasic aufrufe wie folgt:

Code: Alles auswählen

oMasterScriptProviderFactory = createUnoService("com.sun.star.script.provider.MasterScriptProviderFactory")
g_MasterScriptProvider = oMasterScriptProviderFactory.createScriptProvider("")
oScript = g_MasterScriptProvider.getScript("vnd.sun.star.script:mysqlgetdata3.py$main?language=Python&location=user")
RS_Python = oScript.invoke(Array(), Array(), Array())
Msgbox RS_Python(0) (0)
mysqlgetdata3.py enthält:

Code: Alles auswählen

def main():
    iterable = [["a", "b"], ["c", "d"]]
    text = "x-y"

    result = [text.split("-"), *iterable]    


if __name__ == "__main__":
    main()
hierbei müsste Msgbox RS_Python(0) (0) die Rückgabe "x" (ohne "") liefern, tut es aber nicht.


im Übrigen zweifele ich nicht daran das Python das kann, aber ich sage nochmals: ich bin Anfänger und ich brauchte Hilfe für meinen konkreten Datenbank-Code. Worin liegt denn mein Frevel nach etwas zu fragen was wohl offensichtlich geht, aber wo Du mir nicht weiterhelfen möchtest?

Du schriebst mir:
"Und ja, da ist eine Ebene Indexzugriffe ”zu viel” wenn man nur einen Datensatz abfragt, aber Du schreibst ja es soll auch die Möglichkeit geben Abfragen zu machen die mehr als einen Datensatz liefern. Und was man eher nicht will, sind APIs bei denen sich in solchen Fällen die Rückgabewerte in der Struktur unterscheiden, denn dann kommt man in Teufels Küche wenn man eine Abfrage absetzt bei der mehrere Datensätze möglich wären, konkret aber nur einer vorhanden ist."
Darauf habe ich nun, rein rethorisch, unterstellt Python könnte das nicht (obwohl ich das selbst nie glaubte), Deine Antwort hilft mir nun aber nicht weiter weil mein Problem weiterhin genauso besteht wie zu dem Zeitpunkt wo ich den Thread eröffnet habe: ich bin alleine unfähig das Ganze als Python-Code hinzuschreiben. Das ist mein Problem.


Gruß
Stephan
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Stephan_2021: Deine `main()`-Funktion gibt überhaupt nichts an den Aufrufer zurück. `result` ist ja nur eine lokale Variable innerhalb der Funktion. Da steht ein implizites ``return None`` am Ende.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

__blackjack__ hat geschrieben: Montag 20. September 2021, 02:22 @Stephan_2021: Du hast doch bereits so eine Funktion die beides zusammenfasst: `kombiniert()`. Ich würde da auf Python-Seite keine Liste sondern ein Tupel zurückgeben, aber das wird ja auf StarBASIC-Seite dann genau so zu einem Array wie die Liste. Also mit der `convert_to_string()`-Lösung aus dem anderen Beitrag:

Code: Alles auswählen

def kombiniert(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 (
                cursor.column_names,
                [
                    tuple(map(convert_to_string, row))
                    for row in cursor.fetchall()
                ],
            )
Und ja, da ist eine Ebene Indexzugriffe ”zu viel” wenn man nur einen Datensatz abfragt, aber Du schreibst ja es soll auch die Möglichkeit geben Abfragen zu machen die mehr als einen Datensatz liefern. Und was man eher nicht will, sind APIs bei denen sich in solchen Fällen die Rückgabewerte in der Struktur unterscheiden, denn dann kommt man in Teufels Küche wenn man eine Abfrage absetzt bei der mehrere Datensätze möglich wären, konkret aber nur einer vorhanden ist.
Ich habe Deinen Code jetzt getestet und bekommen die Meldung: "...name 'convert_to_string' is not defined...."

Was muss ich tun/ändern/ergänzen?


Gruß
Stephan
Benutzeravatar
__blackjack__
User
Beiträge: 13061
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

__blackjack__ hat geschrieben: Montag 20. September 2021, 15:30 @Stephan_2021: Deine `main()`-Funktion gibt überhaupt nichts an den Aufrufer zurück. `result` ist ja nur eine lokale Variable innerhalb der Funktion. Da steht ein implizites ``return None`` am Ende.
ich bin überzeugt da Deine Antwort richtig ist, aber mir hilft sie nicht weiter, denn ich weiß nicht was ich verändern soll

Aus purer Verzweiflung habe ich jetzt den Code so geändert/gekürzt, weil ich vermutete das könne gehen, tut es aber nicht:

Code: Alles auswählen

def main():
    iterable = [["a", "b"], ["c", "d"]]
    text = "x-y"

    result = [text.split("-"), *iterable]

Gruß
Stephan
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Python-Skript, das Du da gepostet hast, tut nichts, also was erwartest Du als Ausgabe?
Du mußt schon was zurückgeben:

Code: Alles auswählen

def concat():
    iterable = [["a", "b"], ["c", "d"]]
    text = "x-y"
    result = [text.split("-"), *iterable] 
    return result
Stephan_2021
User
Beiträge: 52
Registriert: Sonntag 11. Juli 2021, 09:43

__blackjack__ hat geschrieben: Montag 20. September 2021, 15:37 Die Funktion ist von hier: viewtopic.php?f=23&t=53023&p=393579&hil ... ng#p393579
Danke.

Damit erhalte ich nun (in Starbasic) die gewünschte Ausgabe mittels:

Code: Alles auswählen

Msgbox RS_Python(0) (7) & CHR(13) & RS_Python(1) (0) (7)
und das ist aber 'strukturell' nicht so wie ich das Ergebnis will, denn ich will die richtige Ausgabe, wenn ich so abfrage:

Code: Alles auswählen

Msgbox RS_Python(0) (7) & CHR(13) & RS_Python(1) (7)



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

Sirius3 hat geschrieben: Montag 20. September 2021, 15:42 Das Python-Skript, das Du da gepostet hast, tut nichts, also was erwartest Du als Ausgabe?
Du mußt schon was zurückgeben:

Code: Alles auswählen

def concat():
    iterable = [["a", "b"], ["c", "d"]]
    text = "x-y"
    result = [text.split("-"), *iterable] 
    return result
ja, Entschuldigung, das war wirklich nur meine Blindheit/Dummheit, denn ich verstehe das es das return braucht.


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

Hallo __blackjack__,
Hallo Sirius3,

vielen Dank, ich habe jetzt mit Eurer Hilfe/Anleitung folgende Lösung gefunden:

Code: Alles auswählen

import mysql.connector as mc
from contextlib import closing
from mysql.connector import connect

def kombiniert(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)
            ds = [
                tuple((str(item) if item is not None else "") for item in row)
                for row in cursor.fetchall()
            ]
            
            columns = cursor.column_names
            
            result = [columns, *ds] 
            
            return result

Diese Funktion liefert genau das wonach ich die ganze Zeit gesucht habe.



Gruß
Stephan
Antworten