From 6e3649ed27076f43129ce22da2acc0ecd22328db Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Thu, 9 Jul 2026 12:27:27 +0200 Subject: [PATCH] Port codebase from Python 2 to Python 3 Replace Python 2-only constructs throughout libs/: print statements, ConfigParser -> configparser, sets.Set -> builtin set, string.split -> str.split, xrange -> range, dict.has_key()/.iteritems() -> in/.items(), old-style raise Exception, "msg" syntax, and __cmp__ -> __eq__/__lt__ via functools.total_ordering (Python 3 dropped cmp()/__cmp__, which max()/.sort() rely on). Also cleaned up mixed tab/space indentation that Python 3's stricter tokenizer rejects. Fixed two latent bugs that only surfaced once end-to-end runs were possible under Python 3's stricter error handling: - HandleBestellung() compared G.Anzahl (a bound method) to an int instead of calling G.Anzahl() - silently "worked" under Python 2's permissive cross-type ordering, raises TypeError under Python 3. - LeuteAllein() raised KeyError for VIP-group members, who are never added to the regular Gruppen container; now skips people with no group entry instead of crashing. Verified end-to-end: bin/platz.sh --indir work/test1 and --indir work/test2 (VIP group included) both run to completion and produce correctly formatted output. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 32 +++- README.md | 7 +- libs/Strukturdaten.py | 389 +++++++++++++++++++++--------------------- libs/ga.py | 113 ++++++------ libs/platz.py | 24 +-- requirements.txt | 2 +- 6 files changed, 289 insertions(+), 278 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6145998..d92395a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,11 +10,27 @@ sizes/neighbor relationships, and a penalty scheme. It reads an XML order/guest table layout, runs a small custom GA framework to search for a good seating, and writes the result to text/CSV output files. -**This is Python 2 code** (print statements, `ConfigParser`, `sets.Set`, `UserList`, -`string.split`, old-style `raise Exception, "msg"`, `xrange`, `dict.has_key`, -`dict.iteritems`). It will not run under Python 3 without a 2to3-style port. There is no -build system, test suite, or linter configured in this repo. `requirements.txt` exists but -is empty/commented — everything used is standard library — it's there only so +**This is Python 3 code**, migrated from an original Python 2 implementation (the git +history / old comments may still say "Python 2" — that's now stale). Notable migration +artifacts to be aware of when touching this code: +- `Gruppe`, `Loesung`, and `Sitzplatzverteilung` use `@functools.total_ordering` with + `__eq__`/`__lt__` instead of the old Python 2 `__cmp__` (Python 3 dropped `__cmp__` and + `cmp()` entirely; `max()`/`.sort()` need rich comparisons). +- `sets.Set` → builtin `set`; `ConfigParser` → `configparser`; `string.split` → + `str.split`; `xrange` → `range`; `dict.has_key(k)` → `k in dict`; `dict.iteritems()` → + `dict.items()`; old-style `raise Exception, "msg"` → `raise Exception("msg")`. +- `Strukturdaten.LeuteAllein()` was fixed to skip people with no entry in + `self.Gruppen.GvonPid` (e.g. VIP-group members who were never added to the regular + `Gruppen` container) instead of letting `GruppeVonPid` raise `KeyError` — this was a + latent bug that only surfaced once VIP data (`work/test2`) was actually exercised + end-to-end. +- `HandleBestellung()` had a `G.Anzahl` comparison (`G.Anzahl > 0`) that used to silently + compare a bound method to an int under Python 2's permissive cross-type ordering; + Python 3 raises `TypeError` for that, which is what surfaced it. Fixed to call + `G.Anzahl()`. + +There is no build system, test suite, or linter configured in this repo. `requirements.txt` +exists but is empty/commented — everything used is standard library — it's there only so `bin/install_py.*` has something to install into the `.venv`. ## Running the program @@ -39,8 +55,8 @@ Entry point is `libs/platz.py`, invoked via the scripts in `bin/` (each provided "$PLATZ_LIBS/platz.py"`. This is the actual program entry point (replaces the old `bin/run` / `bin/run.bat`, which hardcoded absolute paths and have been removed). -`libs/platz.py` requires a command-line switch `--indir ` (parsed with `optparse`, -since this is Python 2) pointing at a directory that holds the per-run input files and +`libs/platz.py` requires a command-line switch `--indir ` (parsed with `optparse`) +pointing at a directory that holds the per-run input files and receives the output files — e.g. `work/test1/`, `work/test2/`, or any new folder following the same layout: - `tische.ini` — table definitions: id, `Nummer`, `Hof` (venue/court), `Plaetze` (seats), @@ -79,7 +95,7 @@ Domain-agnostic and reusable in principle: - `Farm` — owns the population (`Pool`/`PoolNeu`), and executes a `Zyklus` action-by-action against instances of a given solution class (passed as `Klasse` plus its constructor args/kwargs). `Bester()` returns the fittest (`max()`) solution found; solutions are ordered - via `__cmp__` on `self.value`. + via `__eq__`/`__lt__` (`@total_ordering`) on `self.value`. ### `Strukturdaten.py` — domain model for this specific seating problem - `Person`, `Gruppe` (group — a `Set` of people, splittable via `teilen()`), `Tisch` (table — diff --git a/README.md b/README.md index 440beb9..943a0c2 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,9 @@ given group memberships, table sizes/neighbor relationships, and a configurable scheme. It reads a guest list (XML) and a table layout (INI), searches for a good seating using a small custom GA framework, and writes the result to text/CSV output files. -**This is Python 2 code** (print statements, `ConfigParser`, `sets.Set`, `UserList`, -`string.split`, old-style `raise Exception, "msg"`, `xrange`, `dict.has_key`, -`dict.iteritems`). There are no third-party dependencies (standard library only), so -`requirements.txt` is empty — it exists only so `bin/install_py` has something to install. +**This is Python 3 code** (migrated from an original Python 2 implementation). There are no +third-party dependencies (standard library only), so `requirements.txt` is empty — it +exists only so `bin/install_py` has something to install. ## Project structure diff --git a/libs/Strukturdaten.py b/libs/Strukturdaten.py index 73b4cbf..0582a77 100755 --- a/libs/Strukturdaten.py +++ b/libs/Strukturdaten.py @@ -9,12 +9,11 @@ __date__ = "$Date: $" __copyright__ = "Copyright (c) 2005 Michael Stangl" __license__ = "Python" -from sets import Set from itertools import chain -from UserList import UserList from random import * -import ConfigParser, os +import configparser, os import copy +from functools import total_ordering from xml.dom import minidom @@ -29,14 +28,14 @@ class Person: def __repr__(self): return ' Person %s: "%s %s %s"' % ( self.Id, self._Titel, self._Vorname, self._Name) - print self.__repr__ +@total_ordering class Gruppe: """enthalt alle Daten zu einer Gruppe""" Art = 'Gruppe' def __init__(self, Id, Personen=[]): self.Id = Id - self._Personen = Set() + self._Personen = set() self._Anzahl = 0 self.PvonId = {} for P in Personen: @@ -47,9 +46,6 @@ class Gruppe: result = result + repr(P.Id) + ',' result = result + ' %s Person(en)' % (self._Anzahl) return result - print self - for P in self._Personen: - print P def Person_dazu(self, P): self._Personen.add(P) self._Anzahl = self._Anzahl + 1 @@ -59,16 +55,16 @@ class Gruppe: self._Personen.remove(P) self._Anzahl = self._Anzahl - 1 else: - raise KeyError, "Person ist nicht enthalten" + raise KeyError("Person ist nicht enthalten") def teilen(self, NeueGroesse=0): if self._Anzahl == 1: - raise IndexError, "Gruppe ist nur noch einer" + raise IndexError("Gruppe ist nur noch einer") if NeueGroesse == 0: NeueGroesse = int(self._Anzahl/2) one = Gruppe( self.Id, self._Personen ) two = Gruppe( self.Id, self._Personen ) two._Personen.clear() - for i in xrange(NeueGroesse): + for i in range(NeueGroesse): two._Personen.add(one._Personen.pop()) one._Anzahl = len(one._Personen) two._Anzahl = len(two._Personen) @@ -78,21 +74,21 @@ class Gruppe: def Anzahl(self): return self._Anzahl def __contains__(self, P): - return P in self._Personen + return P in self._Personen def __iter__(self): for P in self._Personen: - yield(P) - def __cmp__(self,other): - if self._Anzahl < other._Anzahl: return -1 - elif self._Anzahl == other._Anzahl: return 0 - else: return 1 + yield(P) + def __eq__(self,other): + return self._Anzahl == other._Anzahl + def __lt__(self,other): + return self._Anzahl < other._Anzahl def __getattr__(self, attrname): if attrname == 'Anzahl': - return self._Anzahl + return self._Anzahl elif attrname == 'Id': - return self.Id + return self.Id else: - raise AttributeError, attrname + raise AttributeError(attrname) class Tisch(Gruppe): """enthalt alle Daten zu einem Tisch""" @@ -103,7 +99,7 @@ class Tisch(Gruppe): self.Nummer = Nummer self.X_Koordinate = Koordinaten[0] self.Y_Koordinate = Koordinaten[1] - self.Nachbarn = Set(Nachbarliste) + self.Nachbarn = set(Nachbarliste) Gruppe.__init__( self, Id, pids ) def istvoll(self, n=0): if ((self._Anzahl + n) == self._Plaetze ): @@ -126,12 +122,12 @@ class Tisch(Gruppe): return self._Anzahl def Person_dazu(self, P): if (self._Anzahl == self._Plaetze ): - raise IndexError, "Tisch voll" + raise IndexError("Tisch voll") else: Gruppe.Person_dazu( self, P ) def Person_weg(self, P): if (self._Anzahl == 0 ): - raise IndexError, "Tisch schon leer" + raise IndexError("Tisch schon leer") else: Gruppe.Person_weg( self, P ) def teilen(self): @@ -140,7 +136,7 @@ class Tisch(Gruppe): result = ( Gruppe.__repr__( self ) + ' fuer [%s], %s noch frei,' % (self._Plaetze, - self._Plaetze - self._Anzahl) + self._Plaetze - self._Anzahl) ) result = result + " Nachbarn:" for N in chain(self.Nachbarn): @@ -148,20 +144,20 @@ class Tisch(Gruppe): return result def __getattr__(self, attrname): if attrname == 'Anzahl': - return self._Anzahl + return self._Anzahl if attrname == 'Plaetze': return self._Plaetze elif attrname == 'Id': - return self.Id + return self.Id else: - raise AttributeError, attrname + raise AttributeError(attrname) # Containerklassen Personen, Gruppen, Tische, Plaetze class Personen: """ein Pool mit mehreren Personen""" def __init__( self, Personen=[] ): - self._Personen = Set(Personen) + self._Personen = set(Personen) self.PvonId = {} for P in Personen: self.PvonId[ P.Id ] = P @@ -192,7 +188,7 @@ class Gruppen: self.NLeute = 0 for G in chain( GruppenListe ): self.Gruppe_dazu( G ) - print "gruppen hinzugefuegt" + print("gruppen hinzugefuegt") def __iter__( self): sortiert = self.GruppenListe[:] sortiert.sort() @@ -254,8 +250,8 @@ class Tische: for T in sortiert: yield(T) def laden( self, FileName ): - ## print "-- Tische laden" - config = ConfigParser.ConfigParser() + ## print("-- Tische laden") + config = configparser.ConfigParser() config.read( os.path.expanduser(FileName) ) for section in config.sections(): Tid = int(section) @@ -288,14 +284,14 @@ class Plaetze: self.__TIDsNFreie = [] self.__Groesster = Tische.Groesster self.__Tische = Tische - for i in range( self.__Groesster + 1 ): - self.__TIDsNFreie.append( Set() ) + for i in range( self.__Groesster + 1 ): + self.__TIDsNFreie.append( set() ) for T in Tische.TischListe: n_freie = T.freiePlaetze() self.__TIDsNFreie[n_freie].add(T.Id) def __repr__(self): result = "\n-- Tische mit n Freien Plaetzen:\n" - for i in xrange(self.__Groesster+1): + for i in range(self.__Groesster+1): result = result + " " +repr(i) + " :" + repr(self.__TIDsNFreie[i]) + "\n" return result def IdsnFreie(self, n_freie): @@ -381,33 +377,33 @@ class Strafliste: self.NichtNachbar, self.Allein) ) def gibPunkte(self, Typ, Gruppengroesse=0, N_Teilungen=0, freiePlaetze=0): - if cmp(Typ,'allein') == 0: + if Typ == 'allein': return self.Allein - elif cmp(Typ,'nichtNachbar')== 0: + elif Typ == 'nichtNachbar': return self.NichtNachbar - elif cmp(Typ,'Tischbesetzung')== 0: + elif Typ == 'Tischbesetzung': try: return self.TischWert[freiePlaetze] except KeyError: return 0 - elif cmp(Typ,'Trennung')== 0: + elif Typ == 'Trennung': if (N_Teilungen == 0): return 0 elif (N_Teilungen == 1) & (Gruppengroesse < 2): - raise IndexError, "Gruppe sind mindestens zwei Leute" + raise IndexError("Gruppe sind mindestens zwei Leute") elif (N_Teilungen == 2) & (Gruppengroesse < 3): - raise IndexError, "mindestens drei Leute noetig" + raise IndexError("mindestens drei Leute noetig") elif (N_Teilungen > 2): return -50 - if self.GruppenTrennen[N_Teilungen].has_key(Gruppengroesse): + if Gruppengroesse in self.GruppenTrennen[N_Teilungen]: return self.GruppenTrennen[N_Teilungen][Gruppengroesse] else: return self.gibPunkte('Trennung',Gruppengroesse-1, N_Teilungen) else: - raise IndexError, "Bezeichner gibts nicht" + raise IndexError("Bezeichner gibts nicht") def laden( self, FileName ): - print "-- Strafliste laden" - config = ConfigParser.ConfigParser() + print("-- Strafliste laden") + config = configparser.ConfigParser() config.read( os.path.expanduser(FileName) ) self.NichtNachbar = eval(config.get( 'Globals', 'NichtNachbar' )) self.Allein = eval(config.get( 'Globals', 'Allein' )) @@ -417,6 +413,7 @@ class Strafliste: # Sitzplatzverteilung, bestehend aus den Einzelelementen +@total_ordering class Sitzplatzverteilung: """Loesungsvorschlag fuer eine Sitzplatzordung""" @@ -432,11 +429,11 @@ class Sitzplatzverteilung: - zuerst alle kleinsten Gruppen, da man die nicht mehr teilen sollte """ - #print "+ Sitzplatzverteilung\n" + #print("+ Sitzplatzverteilung\n") if Gruppen.NLeute > Tische.NStuehle: - print "Anzahl der Leute:", Gruppen.NLeute - print "Anzahl der Plaetze:", Tische.NStuehle - raise IndexError, "zu wenig Stuehle" + print("Anzahl der Leute:", Gruppen.NLeute) + print("Anzahl der Plaetze:", Tische.NStuehle) + raise IndexError("zu wenig Stuehle") self.MutationsGroesse = 2 self.VIP = VIPs @@ -463,8 +460,8 @@ class Sitzplatzverteilung: self.GruppenSetzen( Gruppen ) # die Sitzplatzverteilung bepunkten - self.bewerten() - print "erzeugt: ", self.value + self.bewerten() + print("erzeugt: ", self.value) def __repr__(self): """Ausgabe der Sitzplatzverteilung an der Konsole""" r = "-- Sitzplatzverteilung:\n" @@ -544,20 +541,20 @@ class Sitzplatzverteilung: self.GruppeEntfernen(G) SchlechteGruppen.append(G) self.GruppenSetzen( SchlechteGruppen ) - self.bewerten() - - print "mutiert: ", self.value + self.bewerten() + + print("mutiert: ", self.value) return self - def __cmp__(self,other): - if self.value < other.value: return -1 - elif self.value == other.value: return 0 - else: return 1 + def __eq__(self,other): + return self.value == other.value + def __lt__(self,other): + return self.value < other.value def bewerten(self): """Errechnet die momentane Wertigkeit der Sitzplaetze""" self.GesamtWertSet( 0 ) # Tische bewerten for T in self.Tische: - #print T + #print(T) n_freie = T.freiePlaetze() Strafpunkte = self.Strafen.gibPunkte( 'Tischbesetzung', freiePlaetze=n_freie ) self.TischWertSet( T, Strafpunkte ) @@ -610,7 +607,7 @@ class Sitzplatzverteilung: # uns setzte sie auf freie Plaetze while len(Gruppenliste) > 0: wahl = choice(['grosse', 'kleine']) - if cmp(wahl,'grosse')== 0: + if wahl == 'grosse': G = Gruppenliste.pop(0) else: G = Gruppenliste.pop(-1) @@ -671,8 +668,8 @@ class Sitzplatzverteilung: Ids = self.Gid_Tid[Gruppe.Id] Ids.add(Tisch.Id) except KeyError: - s = Set() - s.add(Tisch.Id) + s = set() + s.add(Tisch.Id) self.Gid_Tid[Gruppe.Id] = s def GruppeEntfernen(self, Gruppe): """entfernt eine Gruppe von Ihren Tischen""" @@ -751,16 +748,20 @@ class Sitzplatzverteilung: Anzahl_von_Gid = {} Gids = [] for P in Tisch: - G = self.Gruppen.GruppeVonPid(P.Id) try: - Anzahl_von_Gid[G.Id] = Anzahl_von_Gid[G.Id] + 1 + G = self.Gruppen.GruppeVonPid(P.Id) except KeyError: - Anzahl_von_Gid[G.Id] = 1 - for Gid, Anzahl in Anzahl_von_Gid.iteritems(): + # Person gehoert zu keiner Gruppe in self.Gruppen (z.B. VIP) + continue + try: + Anzahl_von_Gid[G.Id] = Anzahl_von_Gid[G.Id] + 1 + except KeyError: + Anzahl_von_Gid[G.Id] = 1 + for Gid, Anzahl in Anzahl_von_Gid.items(): if Anzahl == 1: Gids.append(Gid) return Gids - + # Funktionen fuer mehrere Einheiten (Information) def SchlechteTischeTopX(self, Anzahl=-1): @@ -779,7 +780,7 @@ class Sitzplatzverteilung: def VIPsSetzen(self, VIPListe, VIPs, Tische ): """setze die VIPs auf Ihre Plaetze""" #print " VIPsSetzen:" - for PersonId, TischId in VIPListe.iteritems(): + for PersonId, TischId in VIPListe.items(): #print " VIPsSetzen: PersonId", PersonId #print " VIPsSetzen: TischId", TischId Person = VIPs.PersonVonPid(PersonId) @@ -791,7 +792,7 @@ class Sitzplatzverteilung: RListe=[] Wertigkeit=[] # TODO filter verwenden - for Id, Wert in Liste.iteritems(): + for Id, Wert in Liste.items(): if Wert <= Min: RListe.append(Id) Wertigkeit.append(Wert) @@ -830,13 +831,13 @@ class XMLConfig: GN = Gruppen() VIPGruppe = Gruppe( 0 ) - print '-HandleBestellung' + print('-HandleBestellung') AlleGruppenXML = Bestellung.getElementsByTagName("Gruppe") PID = 0 GID = 0 for GruppeXML in AlleGruppenXML: GID = GID + 1 - print '-- GID ' + repr(GID) + print('-- GID ' + repr(GID)) G = Gruppe( GID ) IsVip = False @@ -844,42 +845,42 @@ class XMLConfig: for Node in GruppeXML.childNodes: if Node.nodeName == 'Tisch': TischNr = self.getText(Node.childNodes) - print 'TischNr:', TischNr + print('TischNr:', TischNr) IsVip = True if Node.nodeName == 'Anzahl': AnzahlReservierungen = int(self.getText(Node.childNodes)) - print 'Anzahl der Reservierungen:', AnzahlReservierungen + print('Anzahl der Reservierungen:', AnzahlReservierungen) IsContingent = True - for Node in GruppeXML.childNodes: + for Node in GruppeXML.childNodes: if Node.nodeName == 'Person': NRes = 0 for i in range(int(AnzahlReservierungen)): NRes = NRes + 1 PID = PID + 1 - print "PID =" + repr(PID) + print("PID =" + repr(PID)) info = self.handlePersonNode( Node ) - - Vorname = info["Vorname"] - Nachname = info["Nachname"] - Titel = info["Titel"] - P = Person( PID, Vorname, Nachname, Titel) + + Vorname = info["Vorname"] + Nachname = info["Nachname"] + Titel = info["Titel"] + P = Person( PID, Vorname, Nachname, Titel) if IsVip == True: PidListe.append( int(PID) ) VipHash[ int(PID) ] = int(TischNr) - VIPGruppe.Person_dazu( P ) + VIPGruppe.Person_dazu( P ) else: PN.PersonAdd( P ) - G.Person_dazu( P ) - if (G.Anzahl > 0) & (IsVip != True): - GN.Gruppe_dazu( G ) + G.Person_dazu( P ) + if (G.Anzahl() > 0) & (IsVip != True): + GN.Gruppe_dazu( G ) if IsVip == True: GID = GID - 1 - print PN - print GN - print VIPGruppe - print VipHash - print '==========' + print(PN) + print(GN) + print(VIPGruppe) + print(VipHash) + print('==========') return [ PN, GN, VIPGruppe, VipHash ] def laden(self, Bestellung): """lese alle Eingangsdaten aus einem XML File ein""" @@ -891,113 +892,113 @@ class XMLConfig: if __name__ == '__main__': - print "\n-- Erzeuge Personen:" + print("\n-- Erzeuge Personen:") P1 = Person( 1, 'Michael', 'Stangl') P2 = Person( 2, 'Magnus', 'Mueller') P3 = Person( 3, 'Bernd Robert', 'Hoehn') P4 = Person( 4, 'Heinz', 'Ruehmann') P5 = Person( 5, 'Franz', 'Bruegge') P6 = Person( 6, 'Hubert', 'K') - print P1 - print P2 - print P3 - print P4 - print P5 - print P6 + print(P1) + print(P2) + print(P3) + print(P4) + print(P5) + print(P6) - print "\n-- Mehrzahl von Personen:" + print("\n-- Mehrzahl von Personen:") PN = Personen([P1, P2, P3, P4, P5, P6]) - print PN + print(PN) - print "\n-- Erzeuge Gruppen mit den Personen:" + print("\n-- Erzeuge Gruppen mit den Personen:") G1 = Gruppe( 1, [P1, P2] ) - G1.Person_dazu( P3 ) + G1.Person_dazu( P3 ) G2 = Gruppe( 2 ) - G2.Person_dazu( P4 ) + G2.Person_dazu( P4 ) G3 = Gruppe( 3, [P5,P6] ) - print "\n-- Stelle Gruppen dar:" - print G1 - print G2 - print G3 + print("\n-- Stelle Gruppen dar:") + print(G1) + print(G2) + print(G3) - print "\n-- Durchlaufe Gruppe 3:" + print("\n-- Durchlaufe Gruppe 3:") for P in G3: - print P + print(P) - print "\n-- Mache Liste der Gruppen:" + print("\n-- Mache Liste der Gruppen:") GN = Gruppen([G1,G2,G3]) - print GN + print(GN) - print "\n-- Durchlaufe Gruppen sortiert:" + print("\n-- Durchlaufe Gruppen sortiert:") for G in GN: - print G + print(G) - print "\n-- Teile Gruppe:" + print("\n-- Teile Gruppe:") [GX, GY] = G1.teilen() - print GX - print GY + print(GX) + print(GY) [GX, GY] = G1.teilen(3) - print GX - print GY + print(GX) + print(GY) - print "\n-- Erzeuge Tische mit Personen:" + print("\n-- Erzeuge Tische mit Personen:") T1 = Tisch( 1, Plaetze=5, Koordinaten=(1,2), pids=[P1, P2], Nachbarliste=[2] ) - print T1 + print(T1) T2 = Tisch( 2, Plaetze=4, Koordinaten=(3,2), Nachbarliste=[1,3] ) - T2.Person_dazu( P3 ) - print T2 - T2.Person_dazu( P4 ) - print T2 - T2.Person_weg( P4 ) - print T2 + T2.Person_dazu( P3 ) + print(T2) + T2.Person_dazu( P4 ) + print(T2) + T2.Person_weg( P4 ) + print(T2) T3 = Tisch( 3, Plaetze=2, Koordinaten=(5,2), Nachbarliste=[2] ) - print "\n-- Ist Tisch 2 voll oder leer?:" - print "ist voll = ", T2.istvoll() - print "ist leer = ", T2.istleer() + print("\n-- Ist Tisch 2 voll oder leer?:") + print("ist voll = ", T2.istvoll()) + print("ist leer = ", T2.istleer()) - print "\n-- Wieviele Plaetze sind an Tisch 1 noch frei ?:" - print "Freie Plaetze = ", T1.freiePlaetze() - print "Besetzte Plaetze = ", T1.besetztePlaetze() + print("\n-- Wieviele Plaetze sind an Tisch 1 noch frei ?:") + print("Freie Plaetze = ", T1.freiePlaetze()) + print("Besetzte Plaetze = ", T1.besetztePlaetze()) - print "\n-- Stelle Tische dar:" - print T1 - print T2 - print T3 - print "\n-- Ist Person Nummer 3 an Tisch 1 ?:" - print P1 in T1 - print "-- Ist Person Nummer 4 an Tisch 1 ?:" - print P4 in T1 + print("\n-- Stelle Tische dar:") + print(T1) + print(T2) + print(T3) + print("\n-- Ist Person Nummer 3 an Tisch 1 ?:") + print(P1 in T1) + print("-- Ist Person Nummer 4 an Tisch 1 ?:") + print(P4 in T1) TE = Tische([T1,T2,T3]) - print TE + print(TE) - print "\n-- Hole Tisch 2 aufgrund seiner Id :" + print("\n-- Hole Tisch 2 aufgrund seiner Id :") TX = TE.vonId( 2 ) - print TX - - print "\n-- Hole Gruppe 2 aufgrund ihrer Id :" - GX = GN.vonId( 2 ) - print GX - - print "\n-- Hole Person 2 aufgrund ihrer Id aus den Gruppen:" - PX = GN.PersonVonPid( 2 ) - print PX - - print "\n-- schau wo noch mindestens 3 Plaetze frei sind:" - info = Plaetze(TE) - print info.mindestensNFreie( 3 ) + print(TX) - print "\n-- schau wo zwei Gruppen bestimmter Groesse nebeneinander sitzen koennen:" - print " zwei neben drei:" + print("\n-- Hole Gruppe 2 aufgrund ihrer Id :") + GX = GN.vonId( 2 ) + print(GX) + + print("\n-- Hole Person 2 aufgrund ihrer Id aus den Gruppen:") + PX = GN.PersonVonPid( 2 ) + print(PX) + + print("\n-- schau wo noch mindestens 3 Plaetze frei sind:") + info = Plaetze(TE) + print(info.mindestensNFreie( 3 )) + + print("\n-- schau wo zwei Gruppen bestimmter Groesse nebeneinander sitzen koennen:") + print(" zwei neben drei:") [TX,TY] = info.NachbarTische(2, 3) - print TX - print TY - print " drei neben zwei:" + print(TX) + print(TY) + print(" drei neben zwei:") [TX,TY] = info.NachbarTische(3, 2) - print TX - print TY + print(TX) + print(TY) P1 = Person( 1, 'Michael', 'Stangl') P2 = Person( 2, 'Magnus', 'Mueller') @@ -1013,37 +1014,37 @@ if __name__ == '__main__': P12 = Person( 12, 'Bla', 'Button') P13 = Person( 13, 'Heinz Harald', 'Frenzen') -# -# T1 T2 T3 T4 -# <3> <5> <3> <4> -# -# G1 G2 G3 G4 -# <3> <2> <2> <6> -# - print "\n-- Mache Gruppen:" +# +# T1 T2 T3 T4 +# <3> <5> <3> <4> +# +# G1 G2 G3 G4 +# <3> <2> <2> <6> +# + print("\n-- Mache Gruppen:") VIPGruppe = ( Gruppe( 1, [P1, P2, P3] ) ) G2 = ( Gruppe( 2, [P4, P5] ) ) G3 = ( Gruppe( 3, [P6, P7] ) ) G4 = ( Gruppe( 4, [P8, P9, P10, P11, P12, P13] ) ) GN = Gruppen([G2,G3,G4]) - print GN + print(GN) - print "\n-- Mache Tische:" + print("\n-- Mache Tische:") T1 = ( Tisch( 1, Plaetze=3, Koordinaten=(1,1), Nachbarliste=[2] ) ) T2 = ( Tisch( 2, Plaetze=5, Koordinaten=(2,1), Nachbarliste=[1,3] ) ) T3 = ( Tisch( 3, Plaetze=3, Koordinaten=(3,1), Nachbarliste=[2,4] ) ) T4 = ( Tisch( 4, Plaetze=4, Koordinaten=(4,1), Nachbarliste=[3] ) ) TE = Tische([T1,T2,T3,T4]) - print TE + print(TE) - print "\n-- Mache Listen:" + print("\n-- Mache Listen:") # Personenid - Tischid VIPListePT = { 3:1, 2:1, 1:1 } - print "VIP Liste:", VIPListePT + print("VIP Liste:", VIPListePT) - # Strafliste - # die Teilung einer grossen Gruppe ist nicht so schlimm + # Strafliste + # die Teilung einer grossen Gruppe ist nicht so schlimm # wie die einer kleinen Teilung = list() Teilung.append({'x':'x'}) @@ -1053,40 +1054,36 @@ if __name__ == '__main__': # Ein Platz an einem Tisch frei ist schlechter als zwei Plaetze frei TischWertigkeiten={ 1:-3, 2:-2, 3:-1 } SL = Strafliste(GruppenTrennen=Teilung, NichtNachbar=-3, Allein=-30, TischWert=TischWertigkeiten) - print SL + print(SL) - print "\n-- Teste Strafliste:" - print "SL.gibPunkte('Trennung', 7,1):", SL.gibPunkte('Trennung', 7,1) - print "SL.gibPunkte('Trennung', 5,1):", SL.gibPunkte('Trennung', 5,1) - print "SL.gibPunkte('Trennung', 3,1):", SL.gibPunkte('Trennung', 3,1) + print("\n-- Teste Strafliste:") + print("SL.gibPunkte('Trennung', 7,1):", SL.gibPunkte('Trennung', 7,1)) + print("SL.gibPunkte('Trennung', 5,1):", SL.gibPunkte('Trennung', 5,1)) + print("SL.gibPunkte('Trennung', 3,1):", SL.gibPunkte('Trennung', 3,1)) try: - print "SL.gibPunkte('Trennung',(1,1):",SL.gibPunkte('Trennung',1,1) + print("SL.gibPunkte('Trennung',(1,1):",SL.gibPunkte('Trennung',1,1)) except IndexError: - print "Gruppe sind mindestens zwei" + print("Gruppe sind mindestens zwei") - print "\n-- Baue Sitzplatzverteilung:" - SV = Sitzplatzverteilung(Tische=TE, Gruppen=GN, Strafen=SL, + print("\n-- Baue Sitzplatzverteilung:") + SV = Sitzplatzverteilung(Tische=TE, Gruppen=GN, Strafen=SL, VIPListe=VIPListePT, VIPs=VIPGruppe) - print SV + print(SV) - print "\n-- Mutiere Sitzplatzverteilung:" + print("\n-- Mutiere Sitzplatzverteilung:") SV.mutieren() - print SV + print(SV) - print "\n-- Gibt die 3 Tische mit der schlechtesten Wertung:" + print("\n-- Gibt die 3 Tische mit der schlechtesten Wertung:") [TischIds, Wertigkeit]=SV.SchlechteTischeTopX(3) - print "TischIds:", TischIds - print "Wertigkeit:", Wertigkeit + print("TischIds:", TischIds) + print("Wertigkeit:", Wertigkeit) - print "\n-- Gibt die Gruppen mit der schlechtesten Wertung:" + print("\n-- Gibt die Gruppen mit der schlechtesten Wertung:") [GruppenIds, Wertigkeit]=SV.SchlechteGruppenTopX(3) - print "GruppenIds:", GruppenIds - print "Wertigkeit:", Wertigkeit + print("GruppenIds:", GruppenIds) + print("Wertigkeit:", Wertigkeit) else: pass - - - - diff --git a/libs/ga.py b/libs/ga.py index b4e0c00..630fb90 100755 --- a/libs/ga.py +++ b/libs/ga.py @@ -4,9 +4,9 @@ Enthaelt alle Datenstrukturen um die einen genetischen Algorithmus darzustellen """ from random import * from itertools import chain -from string import split from copy import deepcopy -import ConfigParser, os +from functools import total_ordering +import configparser, os __author__ = "Michael Stangl" @@ -15,29 +15,30 @@ __date__ = "$Date: $" __copyright__ = "Copyright (c) 2005 Michael Stangl" __license__ = "Python" +@total_ordering class Loesung: """ein einfaches Loesungsobjekt. Taugt um es als Superklasse zu ueberladen""" def __init__(self, *Argumente, **keywords): self.Id = -1 * randint(1,100) self.value = self.Id - print " initialisiere Loesung " + repr(self.Id) + print(" initialisiere Loesung " + repr(self.Id)) def mutieren(self): - print " mutiere Loesung " + repr(self.Id) + print(" mutiere Loesung " + repr(self.Id)) self.Id = -1 * randint(1,100) self.value = self.Id return self def kreuzen(self,obj): - print " kreuze Loesung " + repr(self.Id) + print(" kreuze Loesung " + repr(self.Id)) def laden(self): - print " lade Loesung " + repr(self.Id) + print(" lade Loesung " + repr(self.Id)) def speichern(self): - print " speichere Loesung " + repr(self.Id) + print(" speichere Loesung " + repr(self.Id)) def __repr__(self): - return ' L '+ repr(self.Id) - def __cmp__(self,other): - if self.value < other.value: return -1 - elif self.value == other.value: return 0 - else: return 1 + return ' L '+ repr(self.Id) + def __eq__(self,other): + return self.value == other.value + def __lt__(self,other): + return self.value < other.value class Zyklus: """Definiert wie ein Zyklus ablaufen soll: @@ -45,21 +46,21 @@ class Zyklus: wann sollen Objekte generiert, gekreuzt, mutiert und selektiert werden wieviele sind davon jeweils betroffen """ - def __init__(self, Name='default', - Abfolge='e,m,s', + def __init__(self, Name='default', + Abfolge='e,m,s', Anzahl='10,10,10'): self.Name = Name - self.Abfolge = split(Abfolge, ',') - self.Anzahl = split(Anzahl,',') + self.Abfolge = Abfolge.split(',') + self.Anzahl = Anzahl.split(',') def __repr__(self): return( "Zyklus: %s\n" " Zyklusabfolge: %s\n" " Anzahl : %s\n" - % ( self.Name, - self.Abfolge, + % ( self.Name, + self.Abfolge, self.Anzahl ) ) def laden(self, FileName, ZyklusName): - config = ConfigParser.ConfigParser() + config = configparser.ConfigParser() ZyklusPath= os.path.split( FileName ) ZyklusPath= os.path.join( FileName ) config.read( ZyklusPath ) @@ -102,93 +103,91 @@ class Farm: pass def LoesungenErzeugen(self, N, Klasse, *Arguments, **keywords): """ Erzeuge Loesungen""" - for i in xrange(N): + for i in range(N): L = Klasse( *Arguments, **keywords ) self.Pool.append(L) - #print repr(len(self.Pool)) + ' Loesungsobjekte erzeugt' + #print(repr(len(self.Pool)) + ' Loesungsobjekte erzeugt') def LoesungenKreuzen(self,N): """mache aus zwei Loesungen eine neue""" - #print "+ Loesungen kreuzen" - for i in xrange(N): + #print("+ Loesungen kreuzen") + for i in range(N): self.Pool.append(self.Pool[i].kreuzen()) def LoesungenMutieren(self, N): """veraendere die Loesung, so dass sie hoffentlich besser wird""" - print "+ Loesungen mutieren: ", N - for n in xrange(N): - #print " -"+repr(n+1)+" mal" + print("+ Loesungen mutieren: ", N) + for n in range(N): + #print(" -"+repr(n+1)+" mal") for L in chain(self.Pool): O = deepcopy(L) self.PoolNeu.append(O.mutieren()) - #print self + #print(self) def LoesungenBehalten(self): """Behalte alle Eltern aus dem alten Pool""" - print "+ alten Loesungspool behalten" + print("+ alten Loesungspool behalten") self.PoolNeu = self.PoolNeu + self.Pool - #print self + #print(self) def NeueGeneration(self): """Loesche alle bisherigen Eltern und mache einen neuen Zyklus""" - print "+ Neue Generation erzeugen" + print("+ Neue Generation erzeugen") self.Pool = self.PoolNeu self.PoolNeu = [] - #print self - #print "---------------" + #print(self) + #print("---------------") def LoesungenAuswaehlen(self, N): """Selektiere die besten Loesungen""" - print "+ die besten -", repr(N) +" - Loesungen auswaehlen -" - for i in xrange(N): + print("+ die besten -", repr(N) +" - Loesungen auswaehlen -") + for i in range(N): L = max(self.Pool) self.PoolNeu.append(L) self.Pool.remove(L) - #print self + #print(self) def LoesungenZufaelligAuswaehlen(self, N): """Selektiere zufaellig Loesungen""" - print "+ zufaellig auswaehlen -"+ repr(N) + "-" - for i in xrange(N): + print("+ zufaellig auswaehlen -"+ repr(N) + "-") + for i in range(N): self.PoolNeu.append(choice(self.Pool)) - #print self + #print(self) def __repr__(self): """Drucke die Farm am Bildschirm aus""" return( " gewaehlte Objekte (%d): %s\n" " Elternobjekte (%d) : %s\n" - % ( len(self.PoolNeu), self.PoolNeu, + % ( len(self.PoolNeu), self.PoolNeu, len(self.Pool), self.Pool)) def laden(self): - print "+ laden" + print("+ laden") def speichern(self): - print "+ speichern" + print("+ speichern") def Bester(self): - return max(self.Pool) + return max(self.Pool) if __name__ == '__main__': - print "\n-- Erzeuge Zyklus:" - #Z = Zyklus( Name='Easy', + print("\n-- Erzeuge Zyklus:") + #Z = Zyklus( Name='Easy', #Abfolge='e,s,+,m,j,S,+', #Anzahl='10,5,x,2,x,5,x' #) - Z = Zyklus( Name='Easy', - Abfolge='e,'+'s,z,m,j,z,'*3+'s', - Anzahl='10,'+'5,x,2,x,x,'*3+'1' + Z = Zyklus( Name='Easy', + Abfolge='e,'+'s,z,m,j,z,'*3+'s', + Anzahl='10,'+'5,x,2,x,x,'*3+'1' ) - print Z + print(Z) - print "\n-- Erzeuge Loesungen:" + print("\n-- Erzeuge Loesungen:") L1 = Loesung() L2 = Loesung() L3 = Loesung() - print L1, L2, L3 - print 'L1 > L2 =', L1 > L2 - print 'L2 > L3 =', L2 > L3 - print 'L1 > L3 =', L1 > L3 + print(L1, L2, L3) + print('L1 > L2 =', L1 > L2) + print('L2 > L3 =', L2 > L3) + print('L1 > L3 =', L1 > L3) A=2 B=3 - print "\n-- Erzeuge Farm:" + print("\n-- Erzeuge Farm:") F1 = Farm( Z, Loesung, Arg1=A, Arg2=B, Arg3=L1 ) - print F1 + print(F1) else: pass - - diff --git a/libs/platz.py b/libs/platz.py index 32deaa2..f48f581 100755 --- a/libs/platz.py +++ b/libs/platz.py @@ -11,7 +11,7 @@ __license__ = "Python" from ga import * from Strukturdaten import * -import ConfigParser +import configparser import optparse @@ -33,7 +33,7 @@ if __name__ == '__main__': parser.error( "--indir '%s' ist kein Verzeichnis" % InDir ) # lade Vorgaben fuer Optimierungszyklus und - config = ConfigParser.ConfigParser() + config = configparser.ConfigParser() ConfigPath= os.path.join( os.getenv('PLATZ_CFG'), 'platz.cfg') config.read( os.path.expandvars( ConfigPath )) @@ -55,26 +55,26 @@ if __name__ == '__main__': Z = Zyklus() Z.laden( Zyklusconfig, Zyklusart ) - print Z + print(Z) SL = Strafliste() SL.laden( Strafenconfig ) - print SL + print(SL) TE = Tische() TE.laden( Tischconfig ) - print TE + print(TE) InputFile = XMLConfig() [ PN, GN, VIPGruppe, VIPListePT ] = InputFile.laden( Bestellung ) - print PN - print GN - print VIPGruppe - print VIPListePT + print(PN) + print(GN) + print(VIPGruppe) + print(VIPListePT) - print "\n-- Erzeuge Farm:" + print("\n-- Erzeuge Farm:") F1 = Farm( Z, Sitzplatzverteilung, Tische=TE, Gruppen=GN, Strafen=SL, VIPListe=VIPListePT, VIPs=VIPGruppe ) - #print F1 + #print(F1) S = F1.Bester() - print S + print(S) S.speichern( AusgabeTP, AusgabePT ) else: diff --git a/requirements.txt b/requirements.txt index 00b347d..8f9924e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # Python-Abhaengigkeiten fuer platz # # Aktuell werden nur Module aus der Standardbibliothek verwendet -# (ConfigParser, xml.dom.minidom, sets, UserList, random, ...). +# (configparser, xml.dom.minidom, random, ...). # Keine externen Pakete erforderlich. # # Beispiel: