Seite 1 von 1

Problem zu OOP

Verfasst: Dienstag 30. April 2019, 09:01
von calo
Hallo Leute,

ich komme gerade nicht weiter. Ich habe folgenden Code:

Code: Alles auswählen

class BorderArray(list):
    def __init__(self, a):
        self += a
        
    def find_and_rot(self, n):
        for i, border in enumerate(self):
            if n in border:
                break
        self = self[i:] + self[:i]
        
    def __str__(self):
        for i, n in enumerate(self):
            print "b-%01i:" % i, n

border = BorderArray([[1, 2],[3, 4]])
print border

border.find_and_rot(3)
print border
Als Ergebnis liefert es das:

Code: Alles auswählen

urspruenglich: 
b-0:[1, 2]
b-1:[3, 4]

rotiert: 
b-0:[1, 2]
b-1:[3, 4]
Erwarten würde ich aber für die rotierte Version:

Code: Alles auswählen

rotiert: 
b-0:[3, 4]
b-1:[1, 2]
Wo liegt hier mein Denkfehler :?:

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 09:28
von __blackjack__
@calo: `self` ist ein ganz normaler lokaler Name. Wenn Du dem etwas zuweist, hat das wie bei allen anderen Namen, keinerlei Effekt auf das Objekt was diesem Namen vorher mal zugewiesen war.

Weitere Probleme: Du rufst die `__init__()` der Basisklasse nicht auf.

Du programmierst die `index()`-Methode selbst nach – und das auch noch falsch, denn wenn das gesuchte Objekt nicht enthalten ist, dann wird ein `NameError` ausgelöst, weil es `i` dann gar nicht gibt.

`find_and_rot()` ist ein komischer Name – „finden und verrotten“? Abkürzungen sind doof.

Die `__str__()`-Methode soll eine Zeichenkette *zurückgeben* und nichts *ausgeben*.

Ich würde hier auch nicht von `list` ableiten. Du hast da einen ganzen Haufen Methoden und Operatoren die Du überschreiben müsstest, damit da auch ein `BorderArray` bei heraus kommen würde, oder Du hast einen Datentyp der bei einigen Operationen plötzlich seine (zusätzlichen) Eigenschaften verliert.

Du hast hier ja auch nur eine einzige Methode auf dem neuen Datentyp. Das riecht so ein bisschen nach einer Funktion die übermässig kompliziert umgesetzt wird.

Array ist im Zusammenhang mit Listen auch ein irreführender Name.

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 09:56
von calo
Hallo Blackjack, vielen Dank für die schnelle Antwort.

Leider enthält mein Beispiel gleich mehrere Fehler. Da habe ich wohl nicht aufgepasst. Sorry. Hier nochmal, wie es aus meiner Sicht eigentlich korrekt sein sollte, aber nicht ist:

Code: Alles auswählen

class BorderArray(list):
    def __init__(self, a):
        self += a

    def find_and_rotate(self, n):
        for i, border in enumerate(self):
            if n in border:
                break
        self = self[i:] + self[:i]
        
    def __str__(self):
        txt = ""
        for i, n in enumerate(self):
            txt += "b-%i:" % i + str(n) + "\n"
        return txt

border = BorderArray([[1, 2],[3, 4]])
print "urspruenglich: \n", border

border = border.find_and_rotate(3)
print "rotiert: \n", border
Die eigentliche Frage: Wie kann ich denn die BorderArray-liste von Innen heraus verändern? Vielleicht ist das tatsächlich "von hinten durchs knie ins Auge" geschossen. Einige Eigenschaften der 'list()' benötige ich hier schon und einige zusätzliche. Also, meine eigene Erweiterung von 'list()'.

Was funktioniert ist folgendes:

Code: Alles auswählen

    def find_and_rotate(self, n):
        for i, border in enumerate(self):
            if n in border:
                break
        return BorderArray(self[i:] + self[:i])
Aber das würde andere Attribute bzw. Eigenschaften der Klasse (die hier nicht aufgeführt sind) torpedieren.

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 10:10
von __blackjack__
@calo: Du hast beim neuen Beispiel ausser der `__str__()`-Methode ja nichts geändert. Die angesprochenen Fehler sind alle noch da.

Wenn diese ”Liste” noch mehr Attribute hat, dann ist das IMHO noch mehr ein Grund nicht von `list` zu erben. Das ist ziemlich sicher besser mit Komposition gelöst als mit Vererbung.

Edit: Zum Beispiel so:

Code: Alles auswählen

#!/usr/bin/env python3


class Borders:
    
    def __init__(self, borders):
        self.items = borders

    def __iter__(self):
        return iter(self.items)

    def __str__(self):
        return '\n'.join(
            'b-{}:{}'.format(i, border) for i, border in enumerate(self)
        )

    def find_and_rotate(self, n):
        for i, border in enumerate(self):
            if n in border:
                break
        else:
            raise ValueError('{0!r} is not in any border'.format(n))
        self.items = self.items[i:] + self.items[:i]
        

def main():
    border = Borders([[1, 2], [3, 4]])
    print('urspruenglich:\n', border, sep='')

    border.find_and_rotate(3)
    print('rotiert:\n', border, sep='')


if __name__ == '__main__':
    main()

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 10:42
von calo
Super, vielen Dank :D

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 18:37
von calo
Nachtrag:
@blackjack, ich habe es nun doch noch hinbekommen mit der Vererbung von 'list'. Ist vielleicht nicht elegant, aber es funktioniert:

Code: Alles auswählen

class BorderArray(list):
    
    def __init__(self, borders=[]):
        list.__init__(self, borders)
        
    def find_and_rotate(self, node):
        for i, border in enumerate(self):
            if node in border:
                self[:] = self[i:] + self[:i]
                break
        else:
            raise ValueError("%i not in any border" % node)
        
    def __str__(self):
        txt = ""
        for i, n in enumerate(self):
            txt += "b-%i:" % i + str(n) + "\n"
        return txt
   

def main():
    border = BorderArray([[1, 2],[3, 4], [6, 7, 8, 9]])
    print "urspruenglich: \n", border

    border.find_and_rotate(7)
    print "rotiert: \n", border

    
if __name__ == '__main__':
    main()
Du hattest noch angemerkt, dass ich list.__init__() nicht aufrufe. Da stand ich wohl auf der Leitung. Aber letztendlich hat folgendes geholfen:

Code: Alles auswählen

self[:] = self[i:] + self[:i] 
Vielen Dank nochmals für die Hilfe :)

Re: Problem zu OOP

Verfasst: Dienstag 30. April 2019, 19:08
von nezzcarth
calo hat geschrieben: Dienstag 30. April 2019, 09:56 Die eigentliche Frage: Wie kann ich denn die BorderArray-liste von Innen heraus verändern? Vielleicht ist das tatsächlich "von hinten durchs knie ins Auge" geschossen. Einige Eigenschaften der 'list()' benötige ich hier schon und einige zusätzliche. Also, meine eigene Erweiterung von 'list()'.
Vielleicht geht's nur mir so, aber die Variante, dass du __init__ die Instanz unterschiebst, finde ich schon etwas gewöhnungsbedürftig. Wenn es Vererbung sein soll, dann nimm doch wenigstens collections.UserList. Das ist/war dafür gedacht; es wird zwar teilweise nicht mehr empfohlen, das zu verwenden, persönlich würde ich es in dem Fall aber gegenüber deiner Variante bevorzugen.

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 06:37
von calo
@nezzcarth, UserList kannte ich noch gar nicht. Damit funktioniert es auch recht gut :)

Code: Alles auswählen

from UserList import UserList

class BorderArray2(UserList):

    def __init__(self, borders=[]):
        UserList.__init__(self, borders)

    def rotate(self, node):
        for i, border in enumerate(self):
            if node in border:
                self.data[:] = self.data[i:] + self.data[:i]
                break
        else:
            raise ValueError("%i not in any border" % node)

    def __str__(self):
        txt = ""
        for i, n in enumerate(self.data):
            txt += "b-%i:" % i + str(n) + "\n"
        return txt

def main2():
    border = BorderArray2([[5, 1, 8, 2],[9, 3, 6, 4, 1, 7, 2, 8], [5, 6]])
    print "urspruenglich: \n", border

    border.rotate(3)
    print "rotiert: \n", border
      
if __name__ == '__main__':
    main2()        
Vielen Dank ...

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 07:01
von Sirius3
@calo: gibt es einen bestimmten Grund, neue Sachen noch in Python2 zu programmieren?
[] als Defaultargument ist mit Vorsicht zu genießen. Hier wird dadruch nichts falsch, aber da None auch als Defaultargument taugt, wäre man damit auf der sicheren Seite.
Die Variante, als die eigentiche Arbeit noch nicht in einem tief verschachtelten if steckte, war für mich lesbarer.
Strings mit + zusammenzustücklen ist eigentlich eher BASIC, zumal Du schon teilweise %-Formatierung benutzt.
Was soll die 2 bei main und BorderArray?

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 12:07
von calo
@Sirius3, Die Klasse ist Bestandteil eines größeren Plugins für Abaqus/CAE (wiki, simulia) an dem ich gerade dran bin, einem Auswertetool für Simulationsergebnisse. Leider verwendet der Hersteller hier immer noch Python2. Darauf habe ich keinen Einfluss.

Bzgl. der '2' in main und BorderArray: Das ist einfach nur eine 2. Variante. In Variante 1 habe ich von 'list' geerbt. Diese habe ich in dem Beispiel oben rausgeschmissen.

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 16:24
von nezzcarth
calo hat geschrieben: Donnerstag 2. Mai 2019, 06:37 @nezzcarth, UserList kannte ich noch gar nicht. Damit funktioniert es auch recht gut :)
Wobei du UserList nicht so verwendest, wie es gedacht ist. Der Kniff daran ist ja gerade, dass es ein Attribut '.data' gibt, über das man auf die eigentliche Liste zugreifen kann. Da du jedoch weiterhin deine Version mit dem Übergeben von 'self' an '__init__' benutzt, hat die Verwendung von UserDict keinen Mehrwert.

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 18:16
von Sirius3
@nezzcarth: Dein Anmerkung verstehe ich nicht.

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 18:25
von __blackjack__
@Sirius3: `UserList`-Objekte müssen ein Attribut `data` haben das sich wie eine normale `list` verhält, sonst kann man es gleich sein lassen. Der Witz dabei von `UserList` zu erben ist ja gerade das sich die ganzen Methoden dann so verhalten wie man das erwartet. Da sind die ganzen Methoden, inklusive der magischen, entsprechend implementiert. So das „slicing“ kein `list`-Objekt liefert, sondern ein Objekt von dem Typ der von `UserList` erbt. Dazu erwarten die Methoden die auf `UserList` implementiert sind aber das der Inhalt in einer Liste als Attribut `data` zur Verfügung steht.

Edit: Okay, ich sehe gerade das ja die `UserList.__init__()` aufgerufen wird und damit ein `self.data` angelegt wird. Bitte diesen Beitrag ignorieren… :oops:

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 18:49
von nezzcarth
Ja, ich habe leider nicht richtig geschaut, entschuldigung. :(

Re: Problem zu OOP

Verfasst: Donnerstag 2. Mai 2019, 19:05
von Sirius3
Da die __init__ aber auch nichts sinnvolles macht, kann man sie gleich ganz weglassen.
Bleibt also:

Code: Alles auswählen

class BorderArray(UserList):
    def rotate(self, node):
        for i, border in enumerate(self):
            if node in border:
                break
        else:
            raise ValueError("%i not in any border" % node)
        self[:] = self[i:] + self[:i]

    def __str__(self):
        return ''.join(
            txt += "b-%i: %s\n" % (i, border)
            for i, border in enumerate(self)
        )

Re: Problem zu OOP

Verfasst: Sonntag 5. Mai 2019, 20:18
von ThomasL
Bin gerade auf diesen Beitrag gestoßen und habe mich an diesen Thread erinnert.

https://treyhunner.com/2019/04/why-you- ... in-python/