Seite 1 von 1

Properties definieren

Verfasst: Freitag 6. November 2015, 00:28
von NoPy
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?

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 00:32
von Sirius3
@NoPy: Du willst hier kein Property, sondern Indexzugriff per __getitem__ und __setitem__.

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 00:43
von NoPy
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]

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 00:53
von Sirius3
@NoPy: die Properties liefern dann Objekte, die Indexzugriff erlauben.

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 00:54
von pillmuncher
@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:

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 01:15
von NoPy
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?

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 01:35
von pillmuncher
@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.

Re: Properties definieren

Verfasst: Freitag 6. November 2015, 09:20
von NoPy
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