Text bearbeiten

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.
Antworten
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Diesem Thread bezüglich habe ich noch die Frage, ob es vieleicht eine andere Methode gibt Strings zu bearbeiten. Ein einfaches "insert" find ich so nicht besonders schön:

Code: Alles auswählen

string = "".join(string[:index], char, string[index:])
Andererseits wäre es auch nicht schön den Text als Liste zu speichern und immer wieder zusammen zusetzen, da das bei längeren Texten sicher auch etwas an der Performance nagt.
Ein Byte-Array wäre sicher schneller als eine Liste, nur müsste man dies für die pygame-Methoden auch immer erst wieder in einen String umwandeln.

Gibt es hierzu alternativen für Python ?
Zuletzt geändert von Xynon1 am Dienstag 7. Juni 2011, 12:34, insgesamt 1-mal geändert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn du keine Liste willst, dann mach es so:

Code: Alles auswählen

s = s[:index] + char + s[index:]
Viel anders wirst du es kaum umsetzen können, denn String sind nun mal unveränderlich.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Der zweite Absatz bezog sich nicht mehr auf das obere Beispiel, sry falls das so rüber gekommen ist. Mit in einer Liste speichern meinte ich sowas:

Code: Alles auswählen

class Text:

    def __init__(self, text=""):
        self._text = list(text)

    def get_text(self):
        return "".join(self._text)

    def insert(self, index, char):
        self._text.insert(index, char)
Mit Byte-Array würde das so aussehen:

Code: Alles auswählen

class Text:

    def __init__(self, text=""):
        self._text = array.array("B")
        self._text.fromstring(text)

    def get_text(self):
        return self._text.tostring()

    def insert(self, index, byte):
        self._text.insert(index, byte)
Ich hab es mal schnell getestet und muss sagen, dass das Byte-Array wirklich um längen schneller ist und ich dann auch keine Umwandlung der Tastatureingaben brauche. Ich denke ich werde das so machen oder gibt es Gründe dies nicht zutun?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ganz ehrlich. Das ist doch Mist. Du verkomplizierst die Sache IMHO nur unnötig und müsstest jedes Mal prüfen, ob tatsächlich ein String mit der Länge 1 übergeben wurde, damit es eine ordentliche Methode ist. Spätestens das wiederum wird den erhofften Performance-Gewinn wieder zunichte machen. Ich habe jetzt keine Lust zu messen, aber eventuell ist eine "Übersetzung" des `insert()`-Codes in `self._text = self._text[:index] + char + self._text[index:]` sogar performanter als der Umweg über die Liste. Zumal ja `list()` und `str.join()` auch nochmal "teure" Funktionsaufrufe sind, die ins Gewicht fallen, sofern der Ablauf regelmäßig nur aus "list() -> insert() -> join()" besteht. Auch das jetzige simple Forwarding kostet einen zusätzlichen Aufruf, welcher beim Slicing eingespart wird.

Darf man fragen, für welchen realistischen Anwendungsfall du das überhaupt brauchst?

EDIT: Geschrieben, bevor der Byte-Array Absatz dazu kam.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Xynon1: Auch schon mit `array('c')` probiert? Keine Ahnung, was schneller ist. Aber klingt ja interessant, dass es da solche Unterschiede zur Liste gibt.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Entschuldige, das mit dem Byte-Array war mir auch nur noch schnell eingefallen. Das ich es damit in beiden Fällen verkompliziere ist mir auch klar.
Aber überprüfen ob nur ein Zeichen übergeben wird muss ich in beiden Fällen nicht, da die Texteingabe durch die Tastatur erfolgt und für jede Taste wird auch nur genau ein Event ausgelöst.
Der realistische Anwendungsfall wäre in einem Spiel wohl kaum gegeben, da sicher niemand einen Roman im Chat schreiben wird und andere Text-Felder/Boxen dann wohl eher nicht beschreibbar sind. Ich empfinde es nur als unschön mit den Strings so zu arbeiten.

Kann ich jetzt leider nicht so einfach testen, da ich hier nur python3.2 habe. Das hier war das Testscenario:

Code: Alles auswählen

#!/usr/bin/env python3
import time
import array

loops = 100000
index = 5

a = "Hallo Welt"
b = list(a)
c = array.array("B")
c.fromstring(a)

t = time.time()
for _ in range(loops):
    a = a[:index] + "a" + a[index:]
    
print(time.time() - t)

t = time.time()
for _ in range(loops):
    c.insert(index, 97)
    c.tostring()
    
print(time.time() - t)

t = time.time()
for _ in range(loops):
    b.insert(index, "a")
    "".join(b)
    
print(time.time() - t)
Ausgabe:
5.922000169754028
5.483999967575073 #Bei einem Unicode-Array das doppelte
117.72000002861023
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Lol, ich glaube, so schnell tippt keiner als dass er den Unterschied merken würde. ;)

Wenn du aber eh auf Integer-Werten / Key-Events arbeitest, ist das mit dem Byte-Array vielleicht wirklich keine so schlechte Idee. Als gewisser Nachteil bleibt halt die ständige Erzeugung des Array-Objekts bestehen, denn ein aufeinander folgendes `fromstring()` hängt den neuen String nur hinten dran (außer natürlich, dieses Verhalten ist gewünscht).

Verstehe ich es richtig, dass du eine Art `TextEdit` für deine Pygame-Engine implementieren willst? Coole Idee, falls es das noch nicht gibt.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Wie oben verlinkt ist, baue ich gerade eine komplette GUI :shock: Da ich gerade etwas mit dem Fokus/Input-Handling etwas feststecke, dachte ich erstmal daran etwas mehr Inhalt zu basteln und da das TextField* schon *fertig* ist, war das nächste die TextBox, wo mir diese Überlegung kam.
Das "fromstring" wird AFAIK nur einmal in der Initialiserung und beim expliziten setzen des Textes benötigt. Ansonsten braucht man nur "insert" und "remove", mehr sollte der Nutzer mit dem Text nicht machen können.

*kannst du dir in test.py ansehen
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten