Gültigkeit/Referenz von Instanzvariablen

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
GEEN55
User
Beiträge: 10
Registriert: Mittwoch 16. Januar 2019, 13:41

Hallo,
ich bin Anfänger in Python. Mein Programm verhält sich für mich nicht erklärbar. Vielleicht hat jemand von Euch eine Idee was ich falsch mache.
Meine Klasse sieht wie folgt aus:

Code: Alles auswählen

import matplotlib.image as mpimg

class PolygonTest:
   
    def __init__(self, fileName):
        self.fileName = fileName
        self.__readOriginalFile()
        
    def __readOriginalFile(self):
        self.bild = mpimg.imread(self.fileName)
        return 
  
    def setRefRGB(self,x,y):
        self.refRGB = self.bild[y,x]
        return

    def getRefRGB(self):
        return self.refRGB

    def markLine(self,x,y):
        print("setRefRGB markLine 1:", self.refRGB)   #Testausgabe
        # Linie malen
        self.bild[y,x][0] = 0.0 #R
        self.bild[y,x][1] = 1.0 #G
        self.bild[y,x][2] = 1.0 #B
        print("setRefRGB markLine 2:", self.refRGB)   #Testausgabe
        return

Wenn ich diese nun in folgenden Programm nutze, dann verändert sich der Inhalt der Variable refRGB, obwohl ich dies nicht explizit (nach Initialisierung) tue.

Code: Alles auswählen


from polygon99 import PolygonTest

fileName = 'kurve-klein.png'
po1 = PolygonTest(fileName)
po1.setRefRGB(32,32)
po1.markLine(32,32)

die Ausgabe sieht wie folgt aus
setRefRGB markLine 1: [0. 0. 0.]
setRefRGB markLine 2: [0. 1. 1.]
Als wäre refRGB nur ein Zeiger auf bild[32,32]

Für einen Hinweis wäre ich dankbar.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist so einiges im argen:

- vergiss, dass es doppelte Unterstriche gibt. Die tun nicht, was du glaubst das sie tun, und die Konvention fuer "private" Eigenschaften ist ein einfacher Unterstrich.
- getter und setter schreibt man nicht. In Python gibt es einen Property-Mechanismus, falls aus einem simplen mein_objekt.foo mehr werden muss. Spar dir also getRefRGB.
- die leeren returns kannst du dir ebenso sparen.


Last but not least: ja, Python bindet nur Objekte an Namen. Du kannst dieselbe Liste an tausend Namen binden, und wenn du die Liste veraenderst, dann greifst du durch diese 1000 Namen auch auf die veraenderte Liste zu.

Du musst also eine explizite Kopie der Liste machen, wenn es das ist, was du willst.

Code: Alles auswählen

class PolygonTest:
   
    def __init__(self, fileName):
        self.fileName = fileName
        self._readOriginalFile()
        self.refRGB = [-1, -1, -1]

    def _readOriginalFile(self):
        self.bild = mpimg.imread(self.fileName) 
  
    def setRefRGB(self,x,y):
        self.refRGB = list(self.bild[y,x])
       
    def markLine(self,x,y):
        print("setRefRGB markLine 1:", self.refRGB)   #Testausgabe
        # Linie malen
        self.bild[y,x][0] = 0.0 #R
        self.bild[y,x][1] = 1.0 #G
        self.bild[y,x][2] = 1.0 #B
        print("setRefRGB markLine 2:", self.refRGB)   #Testausgabe
GEEN55
User
Beiträge: 10
Registriert: Mittwoch 16. Januar 2019, 13:41

Ahhhh ... super. Danke für die Hinweise. Da hätte ich mir einen "Wolf" gesucht .....
Nochmals Danke für die schnelle Hilfe!!!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@GEEN55: Zur Namensschreibweise würde ich noch auf den Style Guide for Python Code hinweisen.

Die einzilige ”private” Methode ist IMHO nicht sinnvoll.

Das es sich nur um Namen für das jeweils gleiche Objekt handelt, kann man sich zunutze machen um nicht jedes mal das gleiche Pixel abzufragen in dem man das nur *einmal* am Anfang macht. Und wahrscheinlich kann man sich auch die drei Einzelzuweisungen sparen.

Ungetestet:

Code: Alles auswählen

class PolygonTest:
   
    def __init__(self, filename):
        self.filename = filename
        self.image = mpimg.imread(self.filename)
        self._reference_rgb = [-1, -1, -1]

    def set_reference_rgb(self, x, y):
        self._reference_rgb = list(self.image[y, x])
       
    def mark_line(self, x, y):
        print('reference_rgb mark_line 1:', self._reference_rgb)  # Testausgabe
        # Linie malen
        pixel = self.image[y, x]
        pixel[0] = 0.0 # R
        pixel[1] = 1.0 # G
        pixel[2] = 1.0 # B
        
        # Alternativ könnte auch das hier gehen statt der drei einzelnen
        # Zuweisungen:
        
        self.image[y, x][:] = [0.0, 1.0, 1.0]
        
        print('reference_rgb mark_line 2:', self._reference_rgb)  # Testausgabe
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
GEEN55
User
Beiträge: 10
Registriert: Mittwoch 16. Januar 2019, 13:41

Danke für den Hinweis, insbesondere für den Sytle Guide.
Antworten