Problem zu OOP

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
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

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 :?:
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

Super, vielen Dank :D
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

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 :)
nezzcarth
User
Beiträge: 1733
Registriert: Samstag 16. April 2011, 12:47

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.
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

@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 ...
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

@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?
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

@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.
nezzcarth
User
Beiträge: 1733
Registriert: Samstag 16. April 2011, 12:47

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.
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

@nezzcarth: Dein Anmerkung verstehe ich nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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:
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
nezzcarth
User
Beiträge: 1733
Registriert: Samstag 16. April 2011, 12:47

Ja, ich habe leider nicht richtig geschaut, entschuldigung. :(
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

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)
        )
Benutzeravatar
ThomasL
User
Beiträge: 1377
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Bin gerade auf diesen Beitrag gestoßen und habe mich an diesen Thread erinnert.

https://treyhunner.com/2019/04/why-you- ... in-python/
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Antworten