Properties statt Getters und Setters Properties

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
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

Hallo allerseits,


Ich habe endlich den Absprung von Pflanzen und Bäumen in reale Anwendungen der Objektorientierten Programmierung geschafft und meine erste Klasse Programmiert.

Ohne die Daten Datenkapselung hat sie Funktioniert.

Um es richtig zumachen habe ich mit Datenkapselung begonnen und bin bei Properties statt Getters und Setters gelandet.

Mein erste Frage:

Soll man wirklich Properties statt Getters und Setters verwenden?
https://www.python-kurs.eu/python3_properties.php


Mein zweite Frage:

Mein Code meldet einen TypeError: 'int' object is not callable

Ich sehe aber nicht wo genau.
Anscheinden bei dieser Zeile, aber eienen str übergeben brachte den selben fehler
kanal1.uebertragen(100)


Code:

Code: Alles auswählen

#!/usr/bin/env python3
import smbus
import sys
import time

class I2C_AnalogAusgang:
    """
    I2C_AnalogAusgang 4 Kanal 0-12V
    minimale Wert liegt bei 0x0000 (dec. 0) der Maximale  0x03FF (dec. 1023)
    """
    def __init__(self, Digitalwert, Bus_Adresse, Bus_Kanal):
        """
        Initialisiert ein neues Objekt I2C_AnalogAusgang
        Argumente:
        * Digitalwert (int): ergibt am Physischen Ausgang 0 – 10 Vol
        * Bus_Adresse (int): Bus-Adresse des PCF 8574 in Hex eingeben
        * Bus_Kanal   (int): Bus_Kanal 1 -4 in Hex eingeben
        """
        self.__Digitalwert = Digitalwert
        self.__Bus_Adresse = Bus_Adresse
        self.__Bus_Kanal = Bus_Kanal

    @property
    def Digitalwert(self):
        return self.__Digitalwert

    @Digitalwert.setter
    def uebertragen(self, aenderung):
        try:
            bus = smbus.SMBus(1)
            if aenderung <= 0:
                self.__Digitalwert = 0
            elif aenderung > 1023:
                self.__Digitalwert = 1023
            else:
                self.__Digitalwert = aenderung
            a = self.__Digitalwert
            HBy = int(a / 256)
            LBy = int(a - HBy * 256)
            field = [LBy, HBy]
            bus.write_i2c_block_data(self.__Bus_Adresse, self.__Bus_Kanal, field)
        except (ValueError):
            bus = smbus.SMBus(1)
            self.Digitalwert = aenderung
            a = 0
            HBy = int(a / 256)
            LBy = int(a - HBy * 256)
            field = [LBy, HBy]
            bus.write_i2c_block_data(self.__Bus_Adresse, self.__Bus_Kanal, field)
        except:
            print("Fehler:", sys.exc_info()[0])
            sys.exit("I2C_AnalogAusgang Fehler")

    def __str__(self):
        ausgabe = "Digitalwert: %d \n" % self.__Digitalwert
        ausgabe += "Bus_Adresse: %f \n" % self.__Bus_Adresse
        ausgabe += "Bus_Nummer: %s \n" % self.__Bus_Kanal
        return ausgabe

    def __repr__(self):
        ausgabe = "Digitalwert: %d \n" % self.__Digitalwert
        ausgabe += "Bus_Adresse: %f \n" % self.__Bus_Adresse
        ausgabe += "Bus_Nummer: %s \n" % self.__Bus_Kanal
        return ausgabe


Ausführen:

Code: Alles auswählen

import I2C_AnalogAusgang

def main():
    kanal1 = I2C_AnalogAusgang.I2C_AnalogAusgang(0, 0x58, 0x00)
    print(kanal1)
    kanal1.uebertragen(100)

fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "/root/.pycharm_helpers/pydev/pydevd.py", line 1483, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/root/.pycharm_helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/opt/loxberry/webfrontend/legacy/pythonfiles/0-10V-run.py", line 16, in <module>
    main()
  File "/opt/loxberry/webfrontend/legacy/pythonfiles/0-10V-run.py", line 9, in main
    kanal1.uebertragen(100)
TypeError: 'int' object is not callable
python-BaseException
gruss
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Vorab: Falls du irgendwo gelesen hast, dass doppelte führende Unterstriche bedeuten, dass ein Attribut privat ist: Das stimmt nicht. Doppelte führende Unterstriche lösen sog. Private Name Mangling aus, das man selten mal zur Disambiguierung bei Mehrfachvererbung braucht und das bis auf den Namen nichts gemein hat mit dem, was man sonst unter Privatheit versteht. Statt dessen verwendet man einen einzelnen führenden Unterstruch um anzuzeigen, dass es sich hier um eine Implementierungsdetail handelt, das von außen nicht verwendet werden sollte. Wer es trotzdem verwendet, tut das auf eigene Gefahr. Wirklich privat ist in Python gar nichts, und man braucht das in Pyhon auch nicht.

Zu deiner Frage: Properties dienen nicht zum Information Hiding, sondern dazu, dass man Werte ausgeben bzw. zuweisen kann, die dynamisch erzeugt oder verarbeitet werden. Es hat keinen Vorteil und nur Nachteile, wenn man sowas macht:

Code: Alles auswählen

class Foo:

    def __init__(self, bar):
        self._bar = bar
        
    @property
    def bar(self):
        return self._bar
        
    @bar.setter
    def bar(self, value):
        self._bar = value
statt einfach:

Code: Alles auswählen

class Foo:
    def __init__(self, bar):
        self.bar = bar
Zuletzt geändert von pillmuncher am Dienstag 12. Oktober 2021, 09:26, insgesamt 2-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
pillmuncher
User
Beiträge: 1530
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Zu deiner zweiten Frage: Wenn du uebertragen zum Property machst, dann musst du es auch so verwenden. Also nicht:

Code: Alles auswählen

kanal1.uebertragen(100)
sondern:

Code: Alles auswählen

kanal1.uebertragen = 100
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 18276
Registriert: Sonntag 21. Oktober 2012, 17:20

Variablennamen und Attribute schreibt man komplett klein, genauso wie Methoden.
Settermethoden heißen wie das Attribut.
Wenn man Ganzzahldivision will, benutzt man //, wenn man zusätzlich den Divisionsest will, benutzt man divmod.
Warum heißt der Digitalwert im Setter a?
Wo kann in der Methode ein ValueError auftreten? Und möchte man dann, dass einfach 0 gesetzt wird und das nicht im digitalwert sich wiederspiegel?
sys.exit hat außerhalb der Main-Funktion nichts verloren und dann auch nur, um einen Zahlenwert !=0 zurückzugeben.
__str__ und __repr__ machen genau das selbe, wobei __repr__ gar keine solche Ausgabe haben sollte, sondern etwas zurückgeben, das die Klasse wieder erzeugen kann.
digitalwert, bus_adresse und bus_nummer sollen alles Ints sein, werden mal mit %d, dann mit %f und dann noch per %s formatiert? Warum?

Ist es Absicht, dass der Digitalwert auf dem Bus beim Initilisieren nicht gesetzt wird? Ich glaube nicht.

Code: Alles auswählen

import smbus

class I2C_AnalogAusgang:
    """
    I2C_AnalogAusgang 4 Kanal 0-12V
    minimale Wert liegt bei 0x0000 (dec. 0) der Maximale  0x03FF (dec. 1023)
    """
    def __init__(self, digitalwert, bus_adresse, bus_kanal):
        """
        Initialisiert ein neues Objekt I2C_AnalogAusgang
        Argumente:
        * digitalwert (int): ergibt am Physischen Ausgang 0 – 10 Vol
        * bus_adresse (int): Bus-Adresse des PCF 8574 in Hex eingeben
        * bus_kanal   (int): Bus_Kanal 1 -4 in Hex eingeben
        """
        self._bus_adresse = bus_adresse
        self._bus_kanal = bus_kanal
        self.digitalwert = digitalwert

    @property
    def digitalwert(self):
        return self._digitalwert

    @digitalwert.setter
    def digitalwert(self, aenderung):
        bus = smbus.SMBus(1)
        self._digitalwert = max(0, min(1023, aenderung))
        high, low = divmod(self._digitalwert, 256)
        field = [low, high]
        bus.write_i2c_block_data(self._bus_adresse, self._bus_kanal, field)

    def __str__(self):
        return (
            f"Digitalwert: {self._digitalwert}\n"
            f"Bus-Adresse: {self._bus_adresse}\n"
            f"Bus-Nummer: {self._bus_kanal}\n"
        )

    def __repr__(self):
        return f"{self.__class__.__name__}({self._digitalwert!r}, {self._bus_adresse!r}, {self._bus_kanal!r})"
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

Danke euch, beide Korrekturen funktionieren.


Ich  muss mich mehr mit den Standard Bibliotheken befassen.
max min ist eine tolle Lösung.


__str__ und __repr__ 
war größtenteils  copy paste,
Die Formatierung hätte ich richtig machen können.
sys.exit
nuze ich im Main um am RPI einen Neustart zu erzwingen.
ValueError

Mein Ansatz war, dass bevor mir das ganze Programm abstürzt, wenn falsche Zeichen daher kommen zb als  str gewertet werden, sollte der wert auf 0 gesetzt werden. Bei er nächsten Änderung wäre der Wert wieder richtig. 
Ich gebe dir natürlich recht, dass ich das besser im Main abfange und die Änderung überspringe.

So ist es auch viel übersichtlicher  


Das wird in einem Python Kurs angegeben
In den bisherigen Programmbeispielen haben wir die Attribute, die im Objekt gespeichert sind, immer direkt aus dem Hauptprogramm abgerufen. Das ist jedoch kein besonders guter Programmierstil. Der Grund dafür besteht darin, dass bei der objektorientierten Programmierung die Daten normalerweise gekapselt werden sollten. Das bedeutet, dass du auf Daten eines Objekts nicht von außerhalb zugreifen kannst, sondern nur innerhalb der Klasse, in der es definiert ist.
Attribute auf privat setzen
Für die Datenkapselung ist es zunächst notwendig, den Zugang von außerhalb zu unterbinden. Dazu musst du den Member-Variablen einen doppelten Unterstrich (__) voranstellen.

nochmal danke für den tollen support
Benutzeravatar
ThomasL
User
Beiträge: 1379
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

pumuckll hat geschrieben: Dienstag 12. Oktober 2021, 12:12 Das wird in einem Python Kurs angegeben
In den bisherigen Programmbeispielen haben wir die Attribute, die im Objekt gespeichert sind, immer direkt aus dem Hauptprogramm abgerufen. Das ist jedoch kein besonders guter Programmierstil. Der Grund dafür besteht darin, dass bei der objektorientierten Programmierung die Daten normalerweise gekapselt werden sollten. Das bedeutet, dass du auf Daten eines Objekts nicht von außerhalb zugreifen kannst, sondern nur innerhalb der Klasse, in der es definiert ist.
Attribute auf privat setzen
Für die Datenkapselung ist es zunächst notwendig, den Zugang von außerhalb zu unterbinden. Dazu musst du den Member-Variablen einen doppelten Unterstrich (__) voranstellen.
Wer sowas in einem Python Kurs kommuniziert hat keine Ahnung wie in Python programmiert wird.
Zu 100% ein verkappter Java-Programmierer der meint er muss einen Python Kurs verbrechen.
Bitte Ross und Reiter sprich Autor und Buchtitel nennen oder Online-Link zu dem Kurs, damit wir das hier auf die Schwarze Liste setzen können.
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
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@ThomasL: hat er doch. Der uebliche python-kurs.eu, mit dessen Autor sich __blackjack__ vor einigen Jahren auch schon mal "auseinandergesetzt" hat. Ist hier also schon alles lang und breit diskutiert.
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

ThomasL hat geschrieben: Dienstag 12. Oktober 2021, 15:13
pumuckll hat geschrieben: Dienstag 12. Oktober 2021, 12:12 Das wird in einem Python Kurs angegeben
In den bisherigen Programmbeispielen haben wir die Attribute, die im Objekt gespeichert sind, immer direkt aus dem Hauptprogramm abgerufen. Das ist jedoch kein besonders guter Programmierstil. Der Grund dafür besteht darin, dass bei der objektorientierten Programmierung die Daten normalerweise gekapselt werden sollten. Das bedeutet, dass du auf Daten eines Objekts nicht von außerhalb zugreifen kannst, sondern nur innerhalb der Klasse, in der es definiert ist.
Attribute auf privat setzen
Für die Datenkapselung ist es zunächst notwendig, den Zugang von außerhalb zu unterbinden. Dazu musst du den Member-Variablen einen doppelten Unterstrich (__) voranstellen.
Wer sowas in einem Python Kurs kommuniziert hat keine Ahnung wie in Python programmiert wird.
Zu 100% ein verkappter Java-Programmierer der meint er muss einen Python Kurs verbrechen.
Bitte Ross und Reiter sprich Autor und Buchtitel nennen oder Online-Link zu dem Kurs, damit wir das hier auf die Schwarze Liste setzen können.
das ist Aus diesem Kurs:
https://edley.de/produkt/programmieren-mit-python/



Das Properties statt Getters und Setters hatte ich von dieser Seite weil, ich set und get im oberen Kurs nicht Verstanden hatte

https://www.python-kurs.eu/python3_properties.php

Beide sagen aber bezüglich der Privatisierung das selbe aus.




PS: werde mich hier noch einlesen,
https://www.python.org/dev/peps/pep-0008/
https://www.python.org/dev/peps/pep-000 ... ing-styles
Antworten