Funktion ausführen nachdem Attribut einer Klasseninstance geändert

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
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Hallo Zusammen,

Beim Ändern eines Attributwertes der folgenden Klasse soll immer eine Funktion nach Zuweisung des neuen Wertes ausgeführt werden.
Jetzt kann ich für jedes Atribute wie unten aufgeführt die 'proporties' einzeln schreiben. Bei steigender Zahl der Attribute wird das aber
ziemlich viel Codewiederholung. Kann man das auch kürzer zusammenfassen, so dass 'self.ctrl.send_data_to_view([self.instance_name])
' bei jedem Verändern eines Attributes ausgeführt wird? (egal um welches Attribut es sich handelt)

Code: Alles auswählen

class GenericSpec():

    def __init__(self, instance_name, controller):
        self.instance_name = instance_name
        self.ctrl = controller
       
        self._work_directory = 'No directory choosen...'
        self._nomenclature = ''

    @property
    def work_directory(self):
        return self._work_directory

    @work_directory.setter
    def work_directory(self, work_directory):
        self._work_directory = work_directory
        self.ctrl.send_data_to_view([self.instance_name])

    @property
    def nomenclature(self):
        return self._nomenclature

    @nomenclature.setter
    def nomenclature(self, nomenclature):
        self._nomenclature = nomenclature
        self.ctrl.send_data_to_view([self.instance_name])

__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst dein eigenes property schreiben, welches dann die Methode immer aufruft. Eigentlich brauchst du dazu nur von property zu erben, und dann das setter-Argument noch mal wrappen:

Code: Alles auswählen

#!/usr/bin/env python3


class notifying_property(property):

    def setter(self, func):
        def _w(self, value):
            func(self, value)
            self.tuwas()

        return super().setter(_w)


class Foo:


    def __init__(self):
        self._wert = "was"


    @notifying_property
    def wert(self):
        return self._wert


    @wert.setter
    def wert(self, value):
        self._wert = value


    def tuwas(self):
        print("was getan, und wert ist:", self.wert)



def main():
    foo = Foo()
    print(foo.wert)
    foo.wert = "egal"


if __name__ == '__main__':
    main()
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

@__deets__:
Danke für den Vorschlag. Wenn ich es richtig verstanden habe, dann spare ich mir so erstmal das mehrmalige schreiben des Funktionsaufrufes 'self.tuwas()'. Gibt es denn noch eine Möglichkeit wie ich mir das schreiben der einzelnen proporties ebenfalls sparen könnte?
Also Python führt doch bei jedem Ändern eines Attributs __setattr__ aus...richtig? Könnte man da nicht irgendwie das 'self.tuwas()' unterbringen?
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das geht natuerlich auch.

#!/usr/bin/env python3

Code: Alles auswählen

class Foo:


    def __init__(self):
        self._wert = "was"


    def __getattr__(self, name):
        return getattr(self, "_{}".format(name))


    def __setattr__(self, name, value):
        object.__setattr__(self, "_{}".format(name), value)
        self.tuwas()


    def tuwas(self):
        print("was getan, und wert ist:", self.wert)



def main():
    foo = Foo()
    print(foo.wert)
    foo.wert = "egal"


if __name__ == '__main__':
    main()
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Ja fetzt. :) Besten Dank
BlackJack

@Kniffte: Du könntest Dir einen Descriptor schreiben. So sind Properties letztendlich auch implementiert.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@Kniffte: ein Descriptor implementiert einfach nur die beiden Methoden __get__ und __set__:

Code: Alles auswählen

class watch_attribute(object):
    def __init__(self, attribute_name):
        self.attribute_name = attribute_name

    def __get__(self, instance, cls):
        return getattr(instance, self.attribute_name)

    def __set__(self, instance, value):
        setattr(instance, self.attribute_name, value)
        instance.notify()

class GenericSpec():
    def __init__(self, instance_name, controller):
        self.instance_name = instance_name
        self.ctrl = controller
       
        self._work_directory = 'No directory choosen...'
        self._nomenclature = ''
 
    def notify(self):
        self.ctrl.send_data_to_view([self.instance_name])

    work_directory = watch_attribute('_work_directory')
    nomenclature = watch_attribute('_nomenclature')
Kniffte
User
Beiträge: 64
Registriert: Dienstag 27. September 2016, 11:05

Ok...danke euch beiden.
Für mich war der Vorschlag von __deets__ erstmal leichter nachzuvollziehen und zu verstehen.
Mit dem Beispiel von Sirius versteh ich jetzt auch einen Descriptor.
Spricht grundsätzlich was für oder gegen eine dieser Varianten aus anderer Hinsicht? (Performance, Stil, ...)
Antworten