Properties definieren

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
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Hi, ich bin letztlich immer noch bei meiner Excel- Tabelle. Ich möchte gern so etwas abbilden:

Code: Alles auswählen

my_exl = MyExl('Test.xlsx','Tabelle1')

for spalte in ('A','B','C'):
  for zeile in (1,2,3):
    if my_exl[Spalte,Zeile]==123:
      my_exl[Spalte,Zeile] = 321
Wie muss ich die properties dekorieren, dass es so geht?

Folgendes klappt jedenfalls nicht:

Code: Alles auswählen

class ProTest:
    def __init__(self):
        self.Wert=[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19]]
        
    def get(self,Adresse):
        Spalte,Zeile = Adresse
        return self.Wert[Zeile][Spalte]
        
    def set(self,Adresse,Wert):
        Spalte,Zeile = Adresse
        self.Wert[Zeile][Spalte] = Wert
        
    Zelle = property(get, set, None, "I'm the 'x' property.")


Folgendes läuft fehlerfrei

Code: Alles auswählen

p = ProTest()
print p.get((3,1))
p.set((3,1),'ABC')
Aber egal, welches von Folgendem,

Code: Alles auswählen

print p.Zelle[3,1]
print p.Zelle[3][1]
print p.Zelle[(3,1)]
es kommt immer die Fehlermeldung
TypeError: get() takes exactly 2 arguments (1 given)

Wie muss ich solch ein property deklarieren/dekorieren - was auch immer?
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@NoPy: Du willst hier kein Property, sondern Indexzugriff per __getitem__ und __setitem__.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Vielen Dank

Code: Alles auswählen

class ProTest:
    def __init__(self):
        self.Wert=[[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19]]

    def __getitem__(self, Adresse):
        Spalte,Zeile = Adresse
        return self.Wert[Zeile][Spalte]
        
    def __setitem__(self, Adresse, wert):
        Spalte,Zeile = Adresse
        self.Wert[Zeile][Spalte] = wert
scheint mein akutes Problem tatsächlich erst einmal zu lösen.
Der Aufruf wäre dann

Code: Alles auswählen

p = ProTest()
print p[3,1]
p[3,1] = 12345
Aber was mache ich, wenn ich wirklich ein property haben will?
Also

Code: Alles auswählen

print p.MeinErstesProperty[3,1]
print p.MeinZweitesProperty[3,1]
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@NoPy: die Properties liefern dann Objekte, die Indexzugriff erlauben.
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@NoPy: Properties funktionieren in Python 2.x nur auf New Style Classes. Dazu muss eine Klasse von object erben, andernfalls bleibt sie eine Old Style Class.

Hier eine magische Lösung deines Problems:

Code: Alles auswählen

class Sheet(object):

    def __init__(self):
        self._values = [
            [0, 1, 2, 3, 4],
            [5, 6, 7, 8, 9],
            [10, 11, 12, 13, 14],
            [15, 16, 17, 18, 19]
        ]

    @property
    class cell(object):

        def __init__(self, sheet):
            self._sheet = sheet

        def __getitem__(self, key):
            col, row = key
            return self._sheet._values[col][row]

        def __setitem__(self, key, value):
            col, row = key
            self._sheet._values[col][row] = value


s = Sheet()

print s.cell[2, 3]
Ergebnis:
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Vielen Dank. Aber so vollständig habe ich es nicht verstanden.
An welcher Stelle wird die Verbindung hergestellt zwischen "cell" und "_values"?

Eigentlich kann ich mir das nur erklären, indem beim __init__ auch noch alle "Unterklassen" initialisiert werden und dabei als Parameter implizit selfals Parameter übergeben bekommen hat.
also ein impliziter Aufruf von cell(self)

Habe ich das richtig interpretiert?
Und wenn das so ist, inwieweit hat @property damit etwas zu tun?
Benutzeravatar
pillmuncher
User
Beiträge: 1532
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@NoPy: Naja, halb richtig interpretiert. Es hat nichts mit Unterklassen zu tun. Es würde auch so funktionieren:

Code: Alles auswählen

class Cell(object):

    def __init__(self, sheet):
        self._sheet = sheet

    def __getitem__(self, key):
        col, row = key
        return self._sheet._values[col][row]

    def __setitem__(self, key, value):
        col, row = key
        self._sheet._values[col][row] = value


class Sheet(object):

    def __init__(self):
        self._values = [
            [0, 1, 2, 3, 4],
            [5, 6, 7, 8, 9],
            [10, 11, 12, 13, 14],
            [15, 16, 17, 18, 19]
        ]

    cell = property(Cell)

Cell ist eine Klasse. Klassen sind aufrufbar. Wenn ich eine Klasse aufrufe, wird ein neues Exemplar der Klasse erzeugt und die __init__ Methode aufgerufen. Der erste Parameter dieses Methodenaufrufs wird an die neue Instanz gebunden. Per Konvention wird dazu der Name self verwendet. Wir haben also sowas:

Code: Alles auswählen

>>> class Foo:
...     def __init__(self):
...         print 'in Foo.__init__'
... 
>>> Foo()
in Foo.__init__
<__main__.Foo instance at 0x7f834766e098>
>>> class Bar:
...     def __init__(self, x):
...         print 'in Bar.__init__ got', x
... 
>>> Bar(123)
in Bar.__init__ got 123
<__main__.Bar instance at 0x7f834766e128>
Im zweiten Beispiel rufe ich Bar mit einem Argument auf. In __init__ werden aber zwei Parameter gebunden: self an das Bar-Exemplar und x an das explizit übergebene Argument.

Properties kann man prinzipiell mit jedem aufrufbaren Ding erzeugen. Beim Propertyzugriff wird dieses aufrufbare Ding aufgerufen und ihm eine Referenz auf das Objekt mitgegeben, dessen Property es ist. Falls das aufrufbare Ding eine Klasse ist, bekommt dessen __init__ als erstes - implizites - Argument das Property-Exemplar (gebunden an self) und als zweites Argument das Exemplar der Klasse, deren Property es ist. Ja, es ist etwas magisch.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
NoPy
User
Beiträge: 158
Registriert: Samstag 28. Dezember 2013, 12:39

Also habe ich es im Grunde richtig verstanden bis auf im Wesentlichen folgende Details:
- Die Subklasse wird nicht instanziiert, wenn die Klasse instanziiert wird, sondern erst bei Aufruf
- Wenn sie instanziiert wird, bekommt sie automatisch als zweiten Parameter einen Verweis auf den parent mit
Antworten