Frage zu Listen und Objekten

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.
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

Hall Leute!
Bin seit einiger Zeit mit Python aktiv, aber stelle erst jetzt meine erste Frage im Forum. Ich will Folgendes konstruieren:

Code: Alles auswählen

class A:
    def __init__(self, wert):
        self.a = wert

for i in range(10):
    a=A(i)
Nun will ich auf die Objekte, die ich in der Schleife erzeugt habe, zugreifen. Bis jetzt war mein bester Ansatz, eine Liste zu erzeugen und die Objekte direkt bei Erstellung reinzustellen:

Code: Alles auswählen

class A:
    def __init__(self, wert):
        self.a = wert

Liste=[]

for i in range(10):
    a=A(i)
    Liste.append(a)

print Liste
Das funktioniert zwar prima, aber da ich teils über 10^7 Objekte erzeugen will, ist das langsam. Hat jemand einen besseren Ansatz, wie ich auf die Objekte zugreifen kann? (vlt. alle unterschiedlich benennen, oder kann ich auch ohne den Namen zu kennen auf sie zugreifen?)
Zuletzt geändert von legargeomatrix am Dienstag 27. April 2010, 16:25, insgesamt 1-mal geändert.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Wenn du auf 10⁷ Elemente zugreifen willst, dann musst du sie zwangsläufig irgendwo vorrätig halten - eine Liste könnte da schon richtig sein.

Du nutzt die Klasse später hoffentlich nicht nur, um einen einzigen Wert abzulegen?

Schneller als mit append geht es mittels list comprehension.
Außerdem kannst du statt range xrange verwenden, das ist für 10⁷ auch schneller:

Code: Alles auswählen

liste = [A(i) for i in xrange(10**7)]
"Liste" ist übrigens ein schlechter Name ("liste" auch ...).
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Was verstehst du unter langsam und warum brauchst du so viele Objekte?

Hast du dir numpy angeschaut?
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

Erstmal danke!
numerix hat geschrieben:Wenn du auf 10⁷ Elemente zugreifen willst, dann musst du sie zwangsläufig irgendwo vorrätig halten - eine Liste könnte da schon richtig sein.
Du nutzt die Klasse später hoffentlich nicht nur, um einen einzigen Wert abzulegen?
Ich dachte eher daran, dass sie ja eindeutig definiert sind durch ihren Wert. Ich müsste evtl. noch erwähnen, dass ich mit meiner Klasse einen Baum erstelle, also haben die Objekte (Knoten) Beziehungen untereinander.
numerix hat geschrieben:
Schneller als mit append geht es mittels list comprehension.
Außerdem kannst du statt range xrange verwenden, das ist für 10⁷ auch schneller:

Code: Alles auswählen

liste = [A() for _ in xrange(10**7)]
"Liste" ist übrigens ein schlechter Name ("liste" auch ...).

xrange brauche ich nicht, da die Schleife mit Bedingung abgebrochen wird (wenn der Baum eine bestimmte Tiefe erreicht hat).

Liste ist in der Tat schlecht :)

Ich informiere mich mal um "list comprehension" und sage bescheid
UPDATE: habe mich darüber informiert. Ich glaube, dass ist nicht dass, was ich suche, da meine Objekte durch ein kompliziertes Verfahren erzeugt werden.

Nochmal meine Frage: kann ich auf erstellte Objekte zugreifen, wenn sie keinen eindeutigen Namen haben (oder alle gleich heißen)?
Zuletzt geändert von legargeomatrix am Dienstag 27. April 2010, 16:41, insgesamt 1-mal geändert.
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

jbs hat geschrieben:Was verstehst du unter langsam und warum brauchst du so viele Objekte?

Hast du dir numpy angeschaut?
Zahl der Objekte: ich erstelle einen Baum mit sehr vielen Blättern.
Langsam: den Baum zu erstellen dauert sehr sehr lange, und ich denke, dass es u.a. auch an der Methode meiner Erstellung liegt. Momentan liegt mein Verdacht auf der Liste, die alles verwaltet.

Numpy habe ich mir angeschaut, aber was genau sollte da helfen?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

legargeomatrix hat geschrieben:Nochmal meine Frage: kann ich auf erstellte Objekte zugreifen, wenn sie keinen eindeutigen Namen haben (oder alle gleich heißen)?
Wenn man "Namen" etwas allgemeiner fast: Nein.
Wenn du 10 Instanzen deiner Klasse erzeugst und sie alle an den Namen a bindest, dann kannst du auf die ersten 9 Instanzen über diesen Namen nicht mehr zugreifen.

Wenn du sie in einer Liste ablegst, haben sie zwar keinen Namen im engeren Sinne, aber über die Indizes hast du eben Zugriff.
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

numerix hat geschrieben: Wenn man "Namen" etwas allgemeiner fast: Nein.
Wenn du 10 Instanzen deiner Klasse erzeugst und sie alle an den Namen a bindest, dann kannst du auf die ersten 9 Instanzen über diesen Namen nicht mehr zugreifen.

Wenn du sie in einer Liste ablegst, haben sie zwar keinen Namen im engeren Sinne, aber über die Indizes hast du eben Zugriff.
Könnte man evtl. Namen "künstlich" herstellen, in dem man in die Schleife einen variablen Namen gibt, z.B. in der Form eines Indizes? Also anstatt die Objekte in eine Liste ablagern, sie einfach mit dem entsprechenden indiz aufrufen, z.B. mit a0, a1, a2... anstatt liste[0], liste[1] etc. Wäre das überhaupt schneller?
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

legargeomatrix hat geschrieben:Also anstatt die Objekte in eine Liste ablagern, sie einfach mit dem entsprechenden indiz aufrufen, z.B. mit a0, a1, a2... anstatt liste[0], liste[1] etc. Wäre das überhaupt schneller?
Ja, könntest du diese Datenstruktur nennt sich Dictionary oder Map. Schneller ist das natürlich nicht. Da ist die Liste schon am schnellsten.

Was du machen könntest wäre die Liste vorher zu allozieren.

Code: Alles auswählen

l = [None]*10000000
for i in len(l):
    l[i] = A(i)
Das macht das ganze massiv schneller, da die Liste(die intern ein C-Array ist) nicht ständig vergrößert werden muss. Eine LC wäre übrigens ähnlich schnell.

Code: Alles auswählen

l = [objekt_mit_kompliziertem_verfahren_erzeugen(i) for i in xrange(1000000)]
Allerdings würde ich mich darauf nicht verlassen, da das vermutlich auch von der Implementierung abhängt, ka ob das überall so ist.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

@Darii: Dein gezeigter Code ist gar nicht lauffähig ...

Aber im Ernst: "massiv schneller" wird das dadurch nicht.
Der Flaschenhals ist hier auch nicht das Befüllen oder Anhängen an die Liste, sondern die vielen Instanzbildungen.
lunar

@Darii: Hast Du Zahlen für die „massive“ Beschleunigung?
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Das wär doch mal ne Verwendung für __slots__. Ich habs mal ausprobiert:

Code: Alles auswählen

from time import clock

class A(object):
    __slots__ = ["i"]
    def __init__(self, i):
        self.i = i

l = []

t = clock()
for x in xrange(1000000):
    l.append(A(x))
print clock() - t
Mit slots braucht das ca. 2,7s, ohne 8s. Außerdem wird nur 1/4 des Speichers gebraucht.

Allerdings glaube ich das eine "normale" Datenstruktur (ohne Verwendung von Klassen) am effizientesten wäre. Hat deine Klasse noch irgendwelche Funktionen an sich gebunden, die die Verwendung unbedingt sinnvoll machen? Wenn nein dann solltest du vielleicht alles in einer Liste/Dict abspeichern. Schachteln kannst du auch diese.
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

HerrHagen hat geschrieben:Das wär doch mal ne Verwendung für __slots__. Ich habs mal ausprobiert:

Mit slots braucht das ca. 2,7s, ohne 8s. Außerdem wird nur 1/4 des Speichers gebraucht.

Allerdings glaube ich das eine "normale" Datenstruktur (ohne Verwendung von Klassen) am effizientesten wäre. Hat deine Klasse noch irgendwelche Funktionen an sich gebunden, die die Verwendung unbedingt sinnvoll machen? Wenn nein dann solltest du vielleicht alles in einer Liste/Dict abspeichern. Schachteln kannst du auch diese.
Was macht den genau slots, und warum ist das so viel schneller?
Ich brauche schon meine Baum-DS, da die Knoten eine menge Attribute haben. Ich darf das ganze noch nicht veröffentlichen (Forschungsprojekt) aber hier mal eine Refrenz:

Code: Alles auswählen

class Knoten(object):
    """Knoten-Klasse fuer die Knoten des Spielbaums"""

    def __init__(self, stellung, vater, tiefe):
        self.stellung = stellung
        #die Tiefe entspricht der Runde, in der der Knoten gespielt wurde
        self.tiefe = tiefe
        self.kinder = []
        self.vater = vater
        self.bewertung = None
        self.blatt = False

    #ersetzt del self, denn die pointers muessen entfernt werden
    def delete(self, spielbaum):
        Knoten.get_vater(self).kinder.remove(self)
        self.kinder = []
        spielbaum.remove(self)
        return spielbaum

    def set_bewertung(self, bewertung):
        self.bewertung = bewertung

    def set_tiefe(self,tiefe):
        self.tiefe = tiefe

    def set_kind(self, kind):
        self.kinder.append(kind)

    def set_blatt(self):
        self.blatt = not self.blatt

    def ist_blatt(self):
        return self.blatt

    def get_bewertung(self):
        return self.bewertung

    def get_vater(self):
        return self.vater

    def get_stellung(self):
        return self.stellung

    def get_tiefe(self):
        return self.tiefe

    def get_kinder(self):
        return self.kinder
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Kommst Du aus der Java-Welt oder hast Dich am Open Book von Galileo orintiert? Diese ganzen getter und setter sind absolut unnötig in Python und dazu noch un-pythonisch. Man kann auf die Attribute eines Objektes auch direkt zugreifen.

Code: Alles auswählen

class Foo(object):
    def __init__(self, name):
        self.name = name

f = Foo("Hyperion")
f.name = "Chris"
print f.name
wird zur Ausgabe "Chris" führen.

Also: Weg mit dem ganzen Kram :-)
Zuletzt geändert von Hyperion am Dienstag 27. April 2010, 18:41, insgesamt 1-mal geändert.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

__slots__ sin böse...

[mod]datamodel#slots[/mod]
the more they change the more they stay the same
legargeomatrix
User
Beiträge: 9
Registriert: Dienstag 27. April 2010, 16:11

Hyperion hat geschrieben:Kommst Du aus der Java-Welt oder hast Dich am Open Book von Galileo orintiert? Diese ganzen getter und setter sind absolut unnötig in Python und dazu noch un-pythonisch. Man kann auf die Attribute eines Objektes auch direkt zugreifen.


Also: Weg mit dem ganzen Kram :-)
Ja, dass es auch ohne geht ist mir klar, doch das Projekt wird auch dazu dienen, OOP zu unterrichten, darum die strikte OOP-Schreibweise.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

legargeomatrix hat geschrieben: Ja, dass es auch ohne geht ist mir klar, doch das Projekt wird auch dazu dienen, OOP zu unterrichten, darum die strikte OOP-Schreibweise.
Was haben denn getter und setter mit OOP zu tun? *wunder*

Wie gesagt, bei Java, C# (afaik) und C++ ist das sicher ok. Aber bei Sprachen ohne Zugriffsschutzmechanismen benötigen so etwas nun einmal nicht! Wieso also etwas falsches* lehren?

(* im Sinne von Sprach-Idiomen!)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

legargeomatrix hat geschrieben:Ja, dass es auch ohne geht ist mir klar, doch das Projekt wird auch dazu dienen, OOP zu unterrichten, darum die strikte OOP-Schreibweise.

Nur weil man in Java für jeden Mist getter und setter braucht, ist das doch noch nicht DIE OOP-Schreibweise. Python zeigt doch gerade, dass es auch einfacher und eleganter geht. Oder willst du mit diesem Python-Programm eine Einführung in OOP für Java geben ...
BlackJack

@legargeomatrix: Wenn man mit 10⁷ Objekten arbeitet sollte man halt auch damit leben dass das etwas dauert.

Die Getter und Setter haben nichts mit OOP zu tun und wenn Du für ein Attribut *beides* hast, dann ist das totaler Unsinn weil es vom Effekt das selbe ist wie gleich auf das Attribut direkt zuzugreifen.

In Java macht man diesen Blödsinn statt einfach das Feld ``public`` zu deklarieren, weil man sich sonst später nicht umentscheiden kann einen "berechneten" Zugriff durchzuführen, ohne das sich Quelltext bei den ganzen vorhandenen Zugriffen auch ändern muss. Dieses Problem gibt es Python aber nicht -- dafür ist `property()` da. In Python machst Du Dir also nur völlig unnötige Schreibarbeit die den Quelltext ohne Mehrwert aufbläht und die Laufzeit verlangsamt.

Und wenn Du schon so auf OOP Wert legst, solltest Du Dir die erste Zeile in `delete()` vielleicht noch einmal überlegen ob das wirklich so toll ist die Polymorphie an der Stelle auszuhebeln in dem `Knoten.get_vater()` verwendet wird, egal von welchem Typ `self` wirklich ist. Du setzt da auch `self.kinder` auf eine leere Liste -- allerdings haben die Kinder ja wahrscheinlich einen Verweis auf *diesen* Knoten und der bleibt -- ist das okay?

@Hyperion: Properties gibt's in C# auch.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

numerix hat geschrieben:@Darii: Dein gezeigter Code ist gar nicht lauffähig ...
Meine Güte, dann Pack halt noch nen xrange ums len.
Aber im Ernst: "massiv schneller" wird das dadurch nicht.
Der Flaschenhals ist hier auch nicht das Befüllen oder Anhängen an die Liste, sondern die vielen Instanzbildungen.
Interessant woran das vorhin lag, bei mir kamen vorhin 10 Sek raus, jetzt ist es gleich, muss das morgen nochmal prüfen. Ehrlich gesagt erstaunt es mich, dass append so schnell ist.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

Hier noch mal 'n Benchmark zu dem Thema:

Code: Alles auswählen

import numpy

t = clock()
f1 = [None] * 1000000
for i in xrange(1000000):
    f1[i] = i
print clock() - t

t = clock()
f2 = []
for i in xrange(1000000):
    f2.append(i)
print clock() - t

t = clock()
f3 = numpy.empty(1000000)
for i in xrange(1000000):
    f3[i] = i
print clock() - t
Nimmt sich alles nich viel...

Code: Alles auswählen

0.389669636783
0.550597073092
0.492012252954
Antworten