Verschieden Optimierungsfragen

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
Benutzeravatar
Ande
User
Beiträge: 24
Registriert: Donnerstag 24. März 2011, 18:36

Hallo zusammen,

1.
nehmen wir an:

Code: Alles auswählen

a = 'anfang'
a += machWasMitString(string1)
a += machWasMitString(string2)
etc ...
währe

Code: Alles auswählen

a = []
a.append('anfang')
a.append(machWasMitString(string1))
a.append(machWasMitString(string2))
etc ...
fertig = ''.join(a)
schneller? Oder gibts da noch ne andere herangehensweise, die besser ist?

2.

Code: Alles auswählen

slist = [some_function(elt) for elt in somelist]
s = "".join(slist)
Das funktioniert gut für eine Liste, wenn ich aber diese Funktion auf 2 verschieden Listen abwechselnd anwenden will, geht das auch so ähnlich?

Code: Alles auswählen

list1 = [...]
list2 = [...]# gleiche größe/länge wie list1
list3 = []
for n in range(len(list1)):
    list3.append(list1[n])
    list3.append(list2[n])
BlackJack

@Ande: Zeichenketten durch wiederholtes ``+``/``+=`` zusammen zu setzen ist potentiell ineffizient(er) als es mittels `join()` zu machen. CPython versucht im ersten Fall etwas zu optimieren, aber da kann man sich ganz grundsätzlich nicht drauf verlassen.

Wenn das Beispiel tatsächlich so aussieht, wie Du es gezeigt hast, könntest Du das erste `append()` sparen und den Inhalt gleich in die Liste schreiben.

Code: Alles auswählen

a = ['anfang']
# ...
Bei Deinem zweiten (1.) Beispiel kannst Du `slist` einsparen und die „list comprehension“ durch einen Generatorausdruck ersetzen und den direkt an `join()` übergeben. Eventuell könnte auch `itertools.imap()` nützlich sein.

Code: Alles auswählen

s = ''.join(imap(some_function, some_iterable))
``for i in range(len(obj)):`` ist ein „Anti-Pattern“ in Python. Hier würde man eher `zip()` beziehungsweise `itertools.izip()` verwenden:

Code: Alles auswählen

list3 = list()
for items in izip(list1, list2):
    list3.extend(items)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ad 1: Hier hilft nur Profiling, Profiling und Profiling.
Allerdings sind Strings immutable, d.h. in jedem `+=` wird ein neuer String erzeugt.
Aber wenn du an der Stelle optimieren willst, bist du bei der falschen Sprache.

Ad 2: `itertools.izip`: http://docs.python.org/library/itertool ... tools.izip

Code: Alles auswählen

for t in itertools.izip(list1, list2, list3, ...):
    list0.extend(t)
Edit: Hach schon wieder zu langsam.
needsch
User
Beiträge: 15
Registriert: Donnerstag 22. Dezember 2011, 21:28

Zum Thema String concatenation:

Code: Alles auswählen

"".join(listOfStrings)
ist die beste Variante, weil einfach und verdammt schnell. Ich verweise auf diesen String concatenation benchmark. :)

Zu deiner zweiten Frage:
Ich verstehe nicht ganz, was du meinst mit "Funktion abwechselnd auf 2 Listen anwenden". Dein Beispiel-Code hat mir leider auch nicht eingeleuchtet, weil da nichts abgewechselt wird. :?:
BlackJack

@needsch: `list3` enthält hinterher abwechselnd Elemente aus `list1` und `list2`.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:``for i in range(len(obj)):`` ist ein „Anti-Pattern“ in Python. Hier würde man eher `zip()` beziehungsweise `itertools.izip()` verwenden:

Code: Alles auswählen

list3 = list()
for items in izip(list1, list2):
    list3.extend(items)
Heh, witziger Trick um die Elemente abzuwechseln. Allerdings nicht unbedingt straightforward, und wenn man eine Funktion auf die Elemente anwenden will muss man es dann doch wieder umständlicher machen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Ich würde die Schleife möglichst vermeiden:

Code: Alles auswählen

>>> from itertools import chain, izip
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> list(chain.from_iterable(izip(a, b)))
[1, 4, 2, 5, 3, 6]
Benutzeravatar
Ande
User
Beiträge: 24
Registriert: Donnerstag 24. März 2011, 18:36

Danke schonmal für die Info.

Könnte man map auch mit einer Funktion benutzen, die mehrere Argumente hat, aber von z.B. dem zweiten Argument(eine Liste) nicht jeweils ein Element sondern die ganze Liste an die Funktion weitergegeben werden soll?

Code: Alles auswählen

def funktion( jeweilsElement, liste):
    # code mit element
    # code mit liste

list0 = [...]
list1 = [...]
test = map(funktion, list0, ?)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Da map sich ähnlich wie izip_longest verhält geht es leider nicht sonderlich elegant aber man kann folgendes machen:

Code: Alles auswählen

>>> def foo(item, items):
...     return [item] + items
... 
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> map(foo, a, [b] * len(a))
[[1, 4, 5, 6], [2, 4, 5, 6], [3, 4, 5, 6]]
Funktioniert allerdings nur einwandfrei wenn weder b noch die Elemente in b verändert werden.
Benutzeravatar
Ande
User
Beiträge: 24
Registriert: Donnerstag 24. März 2011, 18:36

Hmm oke danke. Wegen dem profiling, mein programm ist ein PlugIn für eine Software, die Funktionen werden also durch die Software aufgerufen und das Script greift auch auf variablen etc aus der Software zu. Bis jetzt hab ich bei den profilern nur gesehen, dass man zb profiler.run(funktionsname) macht, könnte ich den profiler auch "nebenbei" laufen lassen, ihn also wenn der script ausgeführt wird "anschalten"?
BlackJack

Zwei Alternativen mit `imap()` und `repeat()` aus dem `itertools`-Modul beziehungsweise mit `functools.partial()`:

Code: Alles auswählen

In [312]: list(imap(foo, a, repeat(b)))
Out[312]: [[1, 4, 5, 6], [2, 4, 5, 6], [3, 4, 5, 6]]

In [313]: map(partial(foo, items=b), a)
Out[313]: [[1, 4, 5, 6], [2, 4, 5, 6], [3, 4, 5, 6]]
problembär

BlackJack hat geschrieben:``for i in range(len(obj)):`` ist ein „Anti-Pattern“ in Python. Hier würde man eher `zip()` beziehungsweise `itertools.izip()` verwenden:

Code: Alles auswählen

list3 = list()
for items in izip(list1, list2):
    list3.extend(items)
... was allerdings (im Vergleich zum "Anti-Pattern") ganz schöner Krampf ist.
BlackJack

@problembär: Was ist daran „Krampf“? Es kommt mit weniger Quelltext aus, ist einfach und direkt, und ist allgemeiner, da es keine Sequenzen voraussetzt, sondern mit beliebigen „iterables“ funktioniert. Noch besser ist allerdings der `chain.from_iterable()`-Vorschlag von DasIch, weil der auch wieder ein iterierbares Objekt statt einer Sequenz liefert und damit sogar mit unendlichen Datenströmen klar kommt.
Antworten