Aggregation unter Python

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
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Hallo zusammen,

Es geht um 2 Klassen (Unit und UnitArray) mit folgenden Attributen :
Unit
name : string
width : float
height : float

UnitArray
units : Unit[]

soweit ich weiß nennt sich diese Klassenbeziehung, Aggregation.

Code: Alles auswählen

class Unit():
    def __init__(self, name, width, height ):
        self.width = width
        self.height = height
        self.name = name
        

class UnitArray(dict):
        def __init__(self):
            dict.__init__(self)
            self.units = []
....

unitarray = Unitarray()

....

### aus verschiedenen Listen Daten holen
unit = Unit(NAME[i], WIDTH[i], HEIGHT[i])
### unit in das array speichern 
unitarray.units.append([unit.name, unit.width, unit.height])
            
Das Programm funktioniert gut, und liefert genau das was ich wollte.

Ich hab nun das Gefühl das ich irgendwas falsch verstanden habe:
Die Klassenbeziehung z.B. sieht man in meiner klassendeklaration gar nicht.

ich würde mich über Beispiele freuen.

Danke im Voraus
mfg
driver
BlackJack

Diese Klassenbeziehung sieht man bei dynamischen Sprachen nicht am Quelltext, ausser natürlich an der Bedeutung der Namen und der Dokumentation.

Aber in dem Beispiel ist trotzdem einiges falsch. `UnitArray` erbt von `dict`, warum?

Dann greifst Du von aussen direkt auf das `units`-Attribut zu, so macht das Kapseln in einem eigenen Container-Objekt nicht viel Sinn. Und anstatt das `Unit`-Objekt zu speichern, fügst Du eine Liste mit den einzelnen Attributen der `Unit` an die Liste an. Damit liegt zwischen den beiden Klassen auch keine Aggregations-Beziehung mehr vor.

Wenn das Container-Objekt nicht irgendwelche spezifischen Operationen enthält, ist es nur überflüssige Schreibarbeit. Wenn `UnitArray` keine Methoden bekommt die auf den enthaltenen `Unit`-Objekten operieren, ist die Klasse überflüssig und kann durch etwas generischeres wie eine Liste ersetzt werden.
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

BlackJack hat geschrieben:Diese Klassenbeziehung sieht man bei dynamischen Sprachen nicht am Quelltext, ausser natürlich an der Bedeutung der Namen und der Dokumentation..
genau das wollte ich wissen, danke
BlackJack hat geschrieben:`UnitArray` erbt von `dict`, warum?
ja stimmt, das brauche ich gar nicht .
BlackJack hat geschrieben:Wenn `UnitArray` keine Methoden bekommt die auf den enthaltenen `Unit`-Objekten operieren, ist die Klasse überflüssig und kann durch etwas generischeres wie eine Liste ersetzt werde
dann macht das mehr sinn, wenn die UnitArray z.B. eine Methode enthält, die das Parsen und Speichern von Unitobjekten in das Array units dürchführt.
oder ???

mfg
driver
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Hi BlackJack,

ist es jetzt einigermaßen richtig?
ich habe die Namen geändert.
Ich habe noch mehrere Klassen definiert, die auch in der Art aufgebaut sind.
Das Dürchführen des Programms dauert ziemlich lange (etwa 30 sec.)
Das ganze Programm funktionniert gut . Aber irgendwas mache ich noch falsch.

Es gibt bestimmt einige Sachen, die ich noch verbessern kann.

Code: Alles auswählen

class Cell() :
    def __init__(self):        
            self.name = None
            self.width = None
            self.height = None
            self.port = dict()

class Port() :
    def __int__(self):
        self.name = None
        self.llx = None    #lower left
        self.lly = None
        self.urx = None
        self.ury = None   #upper right
        self.x = None
        self.y = None

class CellArray() :
    def __init__(self):
        self.cells = dict()
    def load(self, lef_datei):
        
        
        
        for match in re.finditer(r'^MACRO (mod\d+).+?^END', lef_datei ,  re.DOTALL | re.MULTILINE ):
            cell = Cell()
            cell.name = match.group(1)
            cell.width = conv*(decimal.Decimal(re.findall(r'SIZE (\d+\.\d+) ',match.group(0))[0]))   # conv ist eine Konstante 
            cell.height = conv*(decimal.Decimal(re.findall(r'SIZE .+BY (\d+\.\d+) ',match.group(0))[0]))
            for match_port in re.finditer(r'PIN (P\d+).+?END P\d+',match.group(0), re.DOTALL ):
                port = Port()
                port.name = match_port.group(1)
                for match_point in re.finditer(r'RECT (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) ;',match_port.group(0)):
                    port.llx= conv*(decimal.Decimal(match_point.group(1)))
                    port.lly = conv*(decimal.Decimal(match_point.group(2)))
                    port.urx = conv*(decimal.Decimal(match_point.group(3)))
                    port.ury = conv*(decimal.Decimal(match_point.group(4)))
                    port.x = (port.llx+port.urx)/2
                    port.y = (port.lly+port.ury)/2
                cell.port[port.name] = {'name':port.name,'llx':port.llx,'lly':port.lly,'urx':port.urx, 'ury':port.ury, 'x':port.x, 'y': port.y}
            self.cells[cell.name]={'name':cell.name,'width':cell.width,'height':cell.height,'port':cell.port}
Mfg driver
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

noch eine Sache.
Die lef_datei erzeuge ich so :

Code: Alles auswählen

lef_file = open('C:/Python25/interconnect_delay/ibm01e_lef.lef' , 'r')
lef_datei=''
for line in lef_file.readlines():
    lef_datei +=line
lef_file.close()
ist das ok. Oder gehts auch einfacher ?

EDIT: verbessert !
Zuletzt geändert von driver am Montag 16. April 2007, 13:35, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

driver hat geschrieben:ist das ok. Oder gehts auch einfacher ?
Ein wenig geht noch:

Code: Alles auswählen

lef_file = open('C:/Python25/interconnect_delay/ibm01e_lef.lef' , 'r')
lef_datei="".join([line for line in lef_file])
lef_file.close()
Ach ja: Ich weiss nicht ob es am Kopieren liegt, aber deine Code-Einrückung ist falsch.

EDIT: Naja, wohl nicht ganz so fit heute:

Code: Alles auswählen

lef_file = open('C:/Python25/interconnect_delay/ibm01e_lef.lef' , 'r')
lef_datei=lef_file.read()
lef_file.close()
driver
User
Beiträge: 20
Registriert: Dienstag 27. März 2007, 12:10

Hi EyDu,
danke erstmal für die schnelle Antwort.
Ich weiss nicht ob es am Kopieren liegt
nein
ist da irgendwas falsch ?


EDIT : ja stimmt .
ich dachte du meinst das Obere :)
BlackJack

Die leeren Klammern nach dem Klassenamen in Klassendefinitionen funktionieren erst ab Python 2.5, davor ist das ein Syntaxfehler. Also am besten ganz weglassen oder von `object` erben.

`Cell` und `Port` werden gar nicht wirklich verwendet und haben keine Funktionalität, nur Daten. Erst werden die Objekte erstellt und dann werden ihre Attribute am Ende doch wieder einzeln in ein Dictionary gepackt und das Objekt wird weggeworfen. Dann kann man sich das auch sparen.

Die Daten die den Zustand eines Objekts ausmachen werden normalerweise von der `__init__()`-Methode entgegengenommen. Und was das Objekt selber berechnen kann, sollte man nicht aussen vorberechnen und setzen. Die Position von einem `Port` kann der auch selber ausrechnen.

Code: Alles auswählen

class Port(object):
    def __int__(self, name, (llx, lly, urx, ury)):
        self.name = name
        self.llx = llx    #lower left 
        self.lly = lly
        self.urx = urx
        self.ury = ury   #upper right
        self.x = (self.llx + self.urx) / 2  # Wirklich?
        self.y = (self.lly + self.ury) / 2
Konstanten werden konventionell in den meisten Programmiersprachen komplett in Grossbuchstaben geschrieben. Dann braucht man auch keinen Kommentar dafür.

Wenn man sowieso nur einen Treffer mit einem regulären Ausdruck erwartet, dann sollte man statt `re.findall()` besser `re.search()` benutzen.

Der `Cell`-Klasse kann man eine `add_port()`-Methode verpassen:

Code: Alles auswählen

class Cell(object):
    def __init__(self, name, width, height):
        self.name = name
        self.width = width
        self.height = heigt
        self.ports = dict()
    
    def add_port(self, port):
        self.ports[port.name] = port
Und die Funktion zum Parsen könnte dann so aussehen:

Code: Alles auswählen

def parse(self, lef_data):
    result = dict()
    for match in re.finditer(r'^MACRO (mod\d+).+?^END',
                             lef_data,
                             re.DOTALL | re.MULTILINE ):
        size_match = re.search(r'SIZE (\d+\.\d) BY (\d+\.\d+)',
                               match.group(0))
        cell = Cell(match.group(1),
                    CONV * Decimal(size_match.group(1)),
                    CONV * Decimal(size_match.group(2)))
        for match_port in re.finditer(r'PIN (P\d+).+?END P\d+',
                                      match.group(0),
                                      re.DOTALL):
            for match_point in re.finditer(r'RECT (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) ;',
                                           match_port.group(0)):
                port = Port(match_port.group(1),
                            (CONV * Decimal(value)
                             for value in match_point.groups()))
            cell.add_port(port)
        result[cell.name] = cell
    return result
Ich persönlich würde die noch in weitere Funktionen aufteilen. Mindestens in eine, die eine Zelle parst und ein `Cell`-Objekt zurückgibt.

Und dann müsste man überlegen wie ein `Cell`-Objekt benutzt werden soll, also zum Beispiel ob man über den Portnamen als Index auf Ports zugreifen können soll, oder wie man am besten über die `Ports`\s einer Zelle iteriert.

Mit dem Einlesen kann man es sich aber auch umständlich machen…

Code: Alles auswählen

lef_file = open('C:/Python25/interconnect_delay/ibm01e_lef.lef' , 'r')
lef_data = lef_file.read()
lef_file.close()
Antworten