Löschen von Items bei for .. in Durchlauf

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.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

Hallo Forum,

ich möchte folgende Operation durchführen.
In einer for-Schleife wird der value geprüft. Entspricht dieser der condition dann soll genau dieser entry gelöscht werden. Das macht er auch nur rutscht der Index runter. Er lässt somit immer welche stehen.

Code: Alles auswählen


for (signr, args) in self.pset.pending:

	if ((unblocks >> signr) & 1):

		self.send(signr, args)

		#self.pset.pending.remove((signr,args))

Meine Frage: Wie kann man über die Elemente iterieren und gleichzeitig löschen ohne, dass ich vom index abhängig bin? Ich habe jetzt schon aus lauter Frust selbst eine doubly linked list implementiert. Aber das muss doch auch mit "Boardmitteln" zu schaffen sein!

Viele Grüße,

Chris
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Du musst ueber eine Kopie iterieren.

Und die Klammern bei ``if`` solltest du besser wegmachen.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

Hallo cofi,

erstmal Danke für Feedback.
Komme aus der C-Ecke und noch frisch in Python. Soweit ich weiß gibt es in Python "nur" Referenzen. Wie erstelle ich eine exakte Kopie?

Viele Grüße,

Chris
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das sieht man am Code und an der Klammer ;)

Ist das eine flache Liste, reicht ``for (signr, args) in self.pset.pending[:]:``, wenn nicht schau dir mal ``copy.deepcopy`` an.

P.S. Ich weiss zwar nicht genau, was du da machst, aber Bitmanipulation ist nicht allzu haeufig in Python-Code und relativ schlecht lesbar, gibt es einen anderen Weg, solltest du den waehlen ;)
BlackJack

@maxwell: In Python würde man auch eher eine neue Liste, ohne die Elemente erstellen, als welche zu entfernen.

In diesem speziellen Fall ist aber vielleicht eine verkettete Liste auch nicht die schlechteste Idee.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

ich habe mir gerade erst "{..}" abgewöhnt... :roll:
aber schön schön...

ich hab es nach folgender art gelöst:

Code: Alles auswählen

	pendings = copy.copy(self.pset.pending)

	unblocks = olMask & nwMask

	# notify all signals which are executed during the blocked time

	for (signr, args) in pendings:

		if ((unblocks >> signr) & 1):

			self.send(signr, args)

			self.pset.pending.remove((signr,args))
Ich glaube das ist nicht gerade gut. Ich werde mir dein Bsp. jetzt zu gemüte führen.
Zur Funktion der Sache:
Ich komme aus dem *nix Kernel-Umfeld. Vorzugsweise Prozesssteuerung. I.d.R reichen in Applikationen eine Hand voll an Signalen aus. Signal sind an kleine Unterbrecherfunktionen gebunden. z.B. int CFGLOAD = 1 führt Funktion zum Laden der Config durch. Oder eben fürs Logging. Signale und ihre Handler-Funktionen liegen in Containern in C sind das halt "structs". Die können dann beliebig ausgetauscht werden. Unterscheidung MODE_0 vs. MODE_1 (user mode) beispielsweise.
Zum "bitshift":
Ich weiß wie viele Signale anfallen dürfen. max NISG = 32
Bit-Operationen sind schneller als Listoperationen! Bevor ich eine Liste mit n+1 int Signalen durchlaufe nur um festzustellen ob es vorhanden ist oder nicht reicht i. R. auch ein shift von a|=1 << "(int) Signalnummer)" oder eben umgekehrt: a >> (int Signalnummer) &1.
Signale können Blockieren wenn man die Ausführungsmaske manipuliert. z.B. BLOCK Signal int SIGTEST = 1. Dann wird der Handler des Signals nicht mehr ausgeführt. siehe Exklusiver Zugriff zweier unterschiedl. Funktionen auf eine und die selbe Ressource (Datei) wenn Signal aus dem gleichen Kontext herraus ausgelöst wird. Also Logger vs. LogWatchDog.

Die Signale sollten jedoch während der blockierten Zeit nicht verloren gehen. Deshalb werden diese als Pending deklariert. Wenn die Verarbeitungsmaske wieder geändert wird. sollen alle aufgelaufenen Signale die nicht mehr blockiert werden ausgeführt werden.

[EDIT]:: noch nen Fehler beseitigt!

Viele Grüße,

Chris
be or not to be
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

ok ok...
ich habs jetzt so gelöst:

Code: Alles auswählen

	for (signr, args) in pendings[:]:

		print signr

		if ((unblocks >> signr) & 1):

			pendings.remove((signr,args))

	#print pendings

Danke an euch ...
Werde versuchen hier bei weiteren Fortschritt meiner Python-Kenntnisse zu Helfen.

Viele Grüße
Chris
be or not to be
BlackJack

@maxwell: Das ist algorithmisch gesehen nicht besonders elegant, weil `remove()` ja wieder von vorne durch die Liste geht und etwas sucht, was man eigentlich ja gerade in den Fingern hatte. Da wäre es vielleicht besser mit `enumerate()` den Index mit zu führen und dann ``del`` zu verwenden.

Das sieht auf jeden Fall ziemlich "unpythonisch" aus. Je nachdem wo das steht würde ich diese hässlichen Details besser verstecken und das Ganze etwas mehr OO zu gestalten. Also zum Beispiel das "Original" durch das hier ersetzen:

Code: Alles auswählen

        self.send_items(self.pset.remove(ol & nw))
Und auf dem Typ von `pset` dann:

Code: Alles auswählen

    def remove(self, unblocked):
        pending = list()
        for item in self.pending:
            if item.match(unblocked):
                yield item
            else:
                pending.append(item)
        self.pending = pending
Statt `item` natürlich ein Name der es besser trifft. Das würde ich auch für die beiden Bitmasken und `pset` vorschlagen.

Falls bei `signr` immer nur ein Bit gesetzt sein sollte, kann man auch ganz einfach eine Liste pro Signalnummer führen!
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

hallo blackjack,

"o" hhhh "o" hhhh höre ich da... :lol:
Das ist algorithmisch gesehen nicht besonders elegant, weil `remove()` ja wieder von vorne durch die Liste geht und etwas sucht, was man eigentlich ja gerade in den Fingern hatte.
genau das war ja meine intention beim ersten versuch die liste zu bearbeiten.
Da wäre es vielleicht besser mit `enumerate()` den Index mit zu führen und dann ``del`` zu verwenden
genau das war mein erster code mit dem resultat das der index rutschte und die exception geworfen wurde. à la out of range

Code: Alles auswählen

  if item.match(unblocked): 
den raffe ich nicht! was hat hier das match zu sagen? dachte das wendet einen regulären ausdruck an.
be or not to be
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

nachschlag:

ich habs jetzt folgender maßen gemacht:

Code: Alles auswählen

	def procmask(self, how, nwMask = None):
		if self.pset is None:
			return 0
		olMask = self.pset.sigmask
		if nwMask is not None:
			if how == SIG_BLOCK:
				self.pset.sigmask = olMask | nwMask
				return olMask
			if how == SIG_UNBLOCK:
				self.pset.sigmask = olMask &~nwMask
			elif how == SET_MASK:
				self.pset.sigmask = nwMask
			else:
				return 0

                        #*************************************************************************

			pendings = list()
			unblocks = olMask & nwMask
			# notify all signals which are executed during the blocked time
			for (signr, args) in self.pset.pending:
				if unblocks >> signr & 1:
					self.send(signr, args)
				else:
					pendings.append((signr, args))
			self.pset.pending = pendings
		return olMask
was ist eigentlich so schlimm bit-operationen zu benutzen???
be or not to be
BlackJack

@maxwell: Das `enumerate()` und Index müssest Du natürlich mit dem Iterieren über eine Kopie der Liste verbinden, damit Du nicht aus der Liste löschst, über die iteriert wird. Aber auch da ist algorithmisch der Weg, den Du jetzt gegangen bist besser, denn das Löschen bedeutet ja auch, das alle Elemente danach umkopiert werden müssen, während bei der aktuellen Lösung jedes Element in amortisiert O(1)-Laufzeit kopiert wird.

Das `match()` auf den `item`-Objekten in meinem Beispiel macht was immer Du dafür implementierst. Zum Beispiel die Bitoperationen.

Jetzt sag bitte nicht, das `ol` und `nw` für `old` und `new` stehen!? :shock:

Das wäre aus dem gleichen Grund schlimm, warum auch Bitoperationen schlecht sind: Es verwirrt den Leser des Quelltextes. Ich habe nämlich gerade festgestellt, dass `item.match()` eher ein `contains()` wäre und man damit sogar noch etwas hübscher den ``in``-Operator überladen könnte.

Methodennamen sollten Tätigkeiten widerspiegeln und möglichst nur *eine*. Hier sind die Tätigkeit*en* als Konstanten über das `how`-Argument angegeben und man muss die Methode mehrfach lesen um zu verstehen was in welchem Fall eigentlich gemacht wird. Und auch bei den Werten gibt es zu viele Sonderfälle. Wenn man nichts verändern will, sollte man die Methode halt einfach nicht aufrufen. Blockieren, Entblockieren und Setzen macht ohne Bitmaske keinen Sinn. Falls die Methode dann trotzdem einen Effekt hat, stecken da ja noch mehr Funktionalitäten drin. Die Anzahl der Ausführungspfade, die man dann verstehen muss verdoppelt sich damit auf 6 beziehungsweise 8, wenn man noch "beliebige" Werte für `how` mit dazu nimmt.

Statt der Bitmasken wären Mengen die offensichtlichste Wahl als Python-Datenstruktur. Wenn man die ganzen Sonderfälle und Mikrooptimierungen rausnimmt, könnte das so aussehen:

Code: Alles auswählen

    def send_pending(self):
        pending = list()
        for signal in self.pset.pending:
            if signal.type not in self.pset.blocked_signals:
                self.send(signal)
            else:
                pending.append(signal)
        self.pset.pending = pending
    
    def set_blocked_signals(self, signals):
        self.pset.blocked_signals = signals
        self.send_pending()

    def block_signals(self, signals):
        self.set_blocked_signals(self.pset.blocked_signals | signals)

    def unblock_signals(self, signals):
        self.set_blocked_signals(self.pset.blocked_signals - signals)
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@ blackjack,

das mag vielleicht altbacken klingen aber sehr interessant diese OOP. (Ja bitte und ich bin auch ohne OOP bisher bestens ausgekommen. :D

Ja "ol" & "nw" stehen für "old" & "new". Was das betrifft, kenne ich eigentlich nur solch "short names". ich mags einfach nicht wenn man - wie ich in unzähligen beispielen der OOP gesehen habe - selbst klassenbezeichnungen mit "dies_ist_meine_klasse_a" oder noch besser "DiesIstMeineKlasseA" bezeichnet. *Huuuuarrrrr
Das wäre aus dem gleichen Grund schlimm, warum auch Bitoperationen schlecht sind: Es verwirrt den Leser des Quelltextes
Arrrrrrr**** da stimme ich mir leider nicht überein. Als Programmierer sollte man schon wissen was da gemacht wird. Auch wenn es auf den ersten Blick nicht ersichtlich ist. Denn das ist ja nun mal kein riesen Quelltext und wir lesen ja keine Märchengeschichte. :P
während bei der aktuellen Lösung jedes Element in amortisiert O(1)-Laufzeit kopiert wird.
jep das ist mir schon klar. Nur störe ich mich momentan immer noch daran, dass hier Referenzen übergeben werden, wenn man aber eine Kopie benötigt. Oder besser, dass die Keys in den listen rutschen. Übrigens die Lösung mit den selbst implementierten Linked Listen funzt einwandfrei. :wink:

Ich werde mir mal deine Lösung ansehen und Strukturen neu überdenken.
Gibts irgendwo nen Coding-Standard Dok?
VG
Chris
Zuletzt geändert von maxwell am Sonntag 12. Juli 2009, 10:51, insgesamt 1-mal geändert.
be or not to be
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Bitte einmal [wiki]PEP 8 (Übersetzung)[/wiki] oder das Original lesen.

Als Programmierer sollte man wissen was los ist, das ist richtig. Man sollte es sich und anderen aber nicht so schwer wie moeglich machen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Na dann herzlich willkommen in der "neuen" Welt ;-) Gewöhne dich einfach daran etwas mehr Code zu schreiben und allen Dingen sinnvolle Namen zu geben.
maxwell hat geschrieben:das mag vielleicht altbacken klingen aber sehr interessant diese OOP
Ich kenne auch einen Informatiker, von dem ich diesen Satz vor 2 Jahren gehört habe. Der war Ende Fünfzig.
maxwell hat geschrieben:Arrrrrrr**** da stimme ich mir leider nicht überein. Als Programmierer sollte man schon wissen was da gemacht wird. Auch wenn es auf den ersten Blick nicht ersichtlich ist. Denn das ist ja nun mal kein riesen Quelltext und wir lesen ja keine Märchengeschichte. :P
Natürlich sollte jeder Programmierer das wissen, aber man muss das Verständnis nicht noch erschweren. Quelltext mag zwar keine Märchengeschichte sein, aber auch sicher kein kryptisches Rätsel.
Das Leben ist wie ein Tennisball.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@EyDu:
Der war Ende Fünfzig.
ich bin ende zwanzig und mein prof meint, dass er es bei mir aufgegen hat. :lol:
seitdem bekomme ich ne extra wurst in sachen hausarbeiten. ich bin zufrieden mit meinen
noten. ;-)

außerdem bin ich von natur aus "tippfaul".
PEPI werde ich mir mal reinziehen...

viele grüße
chris
be or not to be
lunar

maxwell hat geschrieben:Ja "ol" & "nw" stehen für "old" & "new". Was das betrifft, kenne ich eigentlich nur solch "short names". ich mags einfach nicht wenn man - wie ich in unzähligen beispielen der OOP gesehen habe - selbst klassenbezeichnungen mit "dies_ist_meine_klasse_a" oder noch besser "DiesIstMeineKlasseA" bezeichnet. [...] Als Programmierer sollte man schon wissen was da gemacht wird. Auch wenn es auf den ersten Blick nicht ersichtlich ist. Denn das ist ja nun mal kein riesen Quelltext und wir lesen ja keine Märchengeschichte. :P
Wie heißt es so schön: Jeder kann Quelltext für Computer schreiben, gute Programmierer aber schreiben Quelltext für Menschen.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

außerdem bin ich von natur aus "tippfaul".
Da bist du bei Python ja genau richtig. Der Code ist in Python im Schnitt etwa um den Faktor 6-10 kürzer als in C. Die gewonnene Zeit nutzt man halt unter anderem gern dazu ordentliche Namen zu vergeben. :wink:

Führ mal folgenden Code aus:

Code: Alles auswählen

import this
Wenn du wirklich die Sprache lernen willst, musst du dich auf die neue Sprache einlassen und nicht versuchen C in Python zu schreiben.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@lunar
Jeder kann Quelltext für Computer schreiben, gute Programmierer aber schreiben Quelltext für Menschen.
wer das gesagt hat, hat einen sehr schlechten tag gehabt! :lol:

Code: Alles auswählen

	unsigned long i, *s, *m, x;
	int sig = 0;
	
	s = pending->signal.sig;
	m = mask->sig;
	switch (_NSIG_WORDS) {
	default:
		for (i = 0; i < _NSIG_WORDS; ++i, ++s, ++m)
			if ((x = *s &~ *m) != 0) {
				sig = ffz(~x) + i*_NSIG_BPW + 1;
				break;
			}
		break;

	case 2: if ((x = s[0] &~ m[0]) != 0)
			sig = 1;
		else if ((x = s[1] &~ m[1]) != 0)
			sig = _NSIG_BPW + 1;
		else
			break;
		sig += ffz(~x);
		break;

	case 1: if ((x = *s &~ *m) != 0)
			sig = ffz(~x) + 1;
		break;
	}
näääna! :P

@HerrHagen

werde ich machen...[/code]
be or not to be
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

maxwell hat geschrieben:
Jeder kann Quelltext für Computer schreiben, gute Programmierer aber schreiben Quelltext für Menschen.
wer das gesagt hat, hat einen sehr schlechten tag gehabt! :lol:
Und Ahnung hat Martin Fowler auch keine :lol:
Das Leben ist wie ein Tennisball.
BlackJack

@maxwell: Das Du so ganz ohne OO ausgekommen bist, glaube ich nicht. Vielleicht war es Dir nicht bewusst, aber bei vielen guten Softwarearchitekturen findet man die Grundzüge wieder, auch bei Programmiersprachen, die keine spezielle Unterstützung für das Konzept bieten.

Natürlich soll man keine unnötig langen Namen verwenden, aber welche die verständlich sind. Bei `ol` und `nw` hast Du je *einen* Buchstaben gespart, und die Kosten sind, dass zumindest ich nicht auf den ersten Blick die Bedeutung verstanden habe. `DiesIstMeineKlasseA` ist ein schlechter Name, weil der nichts darüber aussagt, was die Klasse denn nun eigentlich repräsentiert.

Grundsätzlich finde ich Abkürzungen in Namen schlecht, solange sie nicht sehr eindeutig, d.h. allgemein bekannt sind. Es verlangsamt den Lesefluss, weil man entweder raten oder suchen muss, ob die Abkürzung vielleicht in irgendeinem Kommentar erklärt wird. Und in gewisser Weise sind Quelltexte "Märchen". Die sollen von Menschen gelesen und verstanden werden, also was ist so schlimm daran, wenn man sie halbwegs flüssig lesen kann? Das ist jetzt kein Plädoyer für superlange und ausschweifende Namen, denn das behindert den Lesefluss dann ja auch wieder. Abr Knsnntn innrhlb vn Wrtrn wglssn, nur um möglichst kurze Namen zu haben, ist Mist. Ich kenne selber noch die Zeiten von BASICs auf Heimrechnern, die nur zwei Buchstaben in Bezeichnern berücksichtigt haben und die Einschränkungen der ersten C-Compiler auf 16 Zeichen. Ich habe heute sogar noch regelmässig mit einer Programmiersprache von IBM aus den 90ern zu tun, die nur die ersten 8 Zeichen von Bezeichnern berücksichtigt. Diese willkürlichen, technischen Beschränkungen gibt es aber heute $GOTT sei Dank bei den modernen Sprachen nicht mehr, und man ist nicht mehr zu kryptischen Kürzeln gezwungen.

Was das tippfaul angeht: Ich verwende einen einfachen Editor, der mit Autovervollständigung aus allen Worten der offenen Texte anbietet. Also noch nicht einmal spezifisch für Python. Da brauche ich jeden "langen" Namen nur einmal in ganzer Schönheit tippen.

Vielleicht sollte man als Programmierer mit den Bitoperationen umgehen können -- Anwendungsentwickler stolpern da bei modernen Sprachen allerdings eher selten drüber -- aber auf der anderen Seite sollte man solche "Spezialitäten" IMHO auch ordentlich "verstecken" und möglichst weit nach "unten" verdrängen. Wenn ich wissen will, ob ein Signaltyp in einer bestimmten Menge an Signaltypen enthalten ist, dann ist ein ``if signal.type in allowed_signals:`` einfach eleganter als ``if ``if allowed_signals >> signal.type & 1:``. Und auch flexibler, denn das ``in`` kann sowohl mit Bitoperationen, als auch mit Mengen umgesetzt werden. Solche Abstraktionen sind nicht neu -- Pascal hat dafür z.B. einen Bitset-Typ, der im Hintergrund genau diese Bitoperationen macht, aber verständlicheren Quelltext ermöglicht.
Antworten