isinstance oder wie vergleicht man typen

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
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

Hallo,

Ich stehe gerade auf dem Schlauch. Folgende zwei Skripte:

FEMesh.py:

Code: Alles auswählen

import AbqDraw
 
class Edge(object):
    def __init__(self):
        self.parent = None
        self.label = None
        self.nodes = None
        
    def create_edge(self):
    	pass

#########################
if __name__ == '__main__':
    print 'within FEMesh.py'
    edge = Edge()
    draw = AbqDraw.drawing()
    draw.draw_edge(edge)
AbqDraw.py:

Code: Alles auswählen

import FEMesh

class drawing(object):
    def __init__(self):
        pass
        
    def draw_edge(self, edge):
        print isinstance(edge, FEMesh.Edge)
        print type(edge)
        print FEMesh.Edge
        
#########################
if __name__ == '__main__':
    print 'within AbqDraw.py'
Wobei FEMesh.py gestartet wird und das zweite Skript lädt.

Bis vor kurzem war das noch eine Datei. Seit ich die zwei Klassen in eigenständige Dateien getrennt habe, funktioniert die Typenprüfung isinstance() nicht mehr so wie ich will:

Code: Alles auswählen

print isinstance(edge, FEMesh.Edge) -> False
print type(edge) -> <class '__main__.Edge'>
print FEMesh.Edge -> <class 'FEMesh.Edge'>
Wie kann ich innerhalb von AbqDraw.py überprüfen, ob edge eine Instanz von FEMesh.Edge ist?
Zuletzt geändert von Anonymous am Donnerstag 10. August 2017, 21:38, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@calo: Wenn Du zwei Module hast die sich gegenseitig importieren, dann stimmt etwas mit der Aufteilung nicht. Dann gehören die ja offensichtlich so eng zusammen, dass sie sich gegenseitig brauchen. So ein zirkulärer Import kann noch ganz andere Probleme nach sich ziehen.
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

Schade. Ich hatte gehofft die Skripte klein zu halten. Ich habe die beiden Skripte wieder zusammengeführt. So funktioniert es natürlich. Das große Skript ist allerdings schon relativ groß (>2000 Zeilen) :roll: .

Vielen Dank.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

@calo: Kannst du das Skript nicht an anderer Stelle trennen? Also zum Beispiel eine Datei für die Klassen und dann jeweils eine für den zugehörigen Code von AbqDraw und FEMesh?
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@calo: Du darfst Deine Module schon aufteilen, nur eben in einer Form, dass ein Abhängigkeitsbaum ensteht und kein Netz. Die Klasse drawing (sollte eigentlich Drawing heißen) ist abhängig von Edge. Dann darf in der Datei, in der die Klasse Edge definiert ist keine Abhängigkeit zu drawing existieren.

In Deinem Beispiel ist die Lösung also ganz einfach:

Code: Alles auswählen

# FEMesh.py
class Edge(object):
    def __init__(self):
        self.parent = None
        self.label = None
        self.nodes = None
       
    def create_edge(self):
        pass

Code: Alles auswählen

# AbqDraw.py
import FEMesh
 
class Drawing(object):
    def __init__(self):
        pass
       
    def draw_edge(self, edge):
        print isinstance(edge, FEMesh.Edge)
        print type(edge)
        print FEMesh.Edge
       
def main():
    edge = FEMesh.Edge()
    draw = Drawing()
    draw.draw_edge(edge)

if __name__ == '__main__':
    main()
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Eine spezielle Anmerkung zum Code: Wenn eine __init__() nur aus pass besteht, dann kann man sie auch weglassen...
BlackJack

Ergänzend dazu: Wenn dann aber keine `__init__()` eine Basisklasse etwas sinnvolles macht um das Objekt zu initialisieren, `object.__init__()` macht da ja eigentlich nichts, dann riecht die Klasse sehr komisch.

Und ich hoffe das sind nicht wirklich zwei Klassen mit jeweils um die 1000 Zeilen Code. :-)

Edit: Typprüfung ist bei einer objektorientierten Lösung ja auch bereits ein „code smell“. Das kann in einigen Fällen sinnvoll sein, aber auch ein Zeichen das der OOP-Entwurf nicht gut ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

calo hat geschrieben: Bis vor kurzem war das noch eine Datei. Seit ich die zwei Klassen in eigenständige Dateien getrennt habe, funktioniert die Typenprüfung isinstance() nicht mehr so wie ich will:

Code: Alles auswählen

print isinstance(edge, FEMesh.Edge) -> False
print type(edge) -> <class '__main__.Edge'>
print FEMesh.Edge -> <class 'FEMesh.Edge'>
Wie kann ich innerhalb von AbqDraw.py überprüfen, ob edge eine Instanz von FEMesh.Edge ist?
Welche Klasse Edge meinst Du denn? Die, welche im Modul FEMesh.py definiert wird, oder diejenige welche dann in Deinem Mainscript definiert wird. Das sind zwei verschiedene Klassen Edge.

Beides müssen Module sein, sonst geht es nicht. Du mußt also ein Scrcipt haben, welches FEMesh importiert.

Also ein Startscript:

Code: Alles auswählen

import FEMesh
FEMesh.main()
FEMesh.py

Code: Alles auswählen

import AbqDraw 

class Edge(object):
    def __init__(self):
        self.parent = None
        self.label = None
        self.nodes = None
       
    def create_edge(self):
        pass

def main():
    print 'within FEMesh.py'
    edge = Edge()
    draw = AbqDraw.drawing()
    draw.draw_edge(edge)
Und AbqDraw.py kann so bleiben, wie es ist.

Das war kein Problem kreuzweise Kopplung sondern Unterschied zwischen Mainscript und Modul
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Es gibt noch eine andere einfache Möglichkeit.

AbqDraw.py

Code: Alles auswählen

class Some_Class:
    Edge = None

FEMesh = Some_Class()
 
class drawing(object):
    def __init__(self):
        pass
       
    def draw_edge(self, edge):
        print isinstance(edge, FEMesh.Edge)
        print type(edge)
        print FEMesh.Edge
       
#########################
if __name__ == '__main__':
    print 'within AbqDraw.py'
FEMesh.py

Code: Alles auswählen

import AbqDraw
 
class Edge(object):
    def __init__(self):
        self.parent = None
        self.label = None
        self.nodes = None
       
    def create_edge(self):
        pass

AbqDraw.FEMesh.Edge = Edge
 
#########################
if __name__ == '__main__':
    print 'within FEMesh.py'
    edge = Edge()
    draw = AbqDraw.drawing()
    draw.draw_edge(edge)
Dabei ist natürlich FEMesh.py als Mainscript zu starten
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: was Du da veranstaltest ist nicht einfach, sondern einfach nur unsinnig. Natürlich kann man alles so hinbiegen, dass es irgendwie funktioniert, aber das wird niemals ein klar strukturiertes, wartbares oder testbares Programm. Kreuzimporte sind immer ein Zeichen dafür, dass man sich bei der Aufteilung in Module nicht genug Gedanken gemacht, bzw. das Problem noch nicht völlig verstanden hat.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: was Du da veranstaltest ist nicht einfach, sondern einfach nur unsinnig. Natürlich kann man alles so hinbiegen, dass es irgendwie funktioniert, aber das wird niemals ein klar strukturiertes, wartbares oder testbares Programm. Kreuzimporte sind immer ein Zeichen dafür, dass man sich bei der Aufteilung in Module nicht genug Gedanken gemacht, bzw. das Problem noch nicht völlig verstanden hat.
Das in meinem vorigen Post ist aber gar kein Kreuzimport
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das wird wieder die selbe Diskussion, wie mit globalen Zuständen, die nicht nur dadurch enstehen, dass irgendwer »global« benutzt. Kreuzimporte entstehen nicht nur, dass jemand »import« benutzt, sondern auch, wenn ich von einem anderen Modul aus Objekte als globale Variable in ein anderes Modul injiziere. Bei einem »import« ist noch relativ klar, was passiert, bei dem was Du da veranstaltest hat niemand eine Chance, nachzuvollziehen, woher denn das Edge in AbqDraw herkommt. Das ist Verschleierung und hat nichts mit programmieren zu tun.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3: das mit der globalen Variablen, vergessen wir besser. Aber Kreuzimporte lassen sich oft nicht vermeiden, wenn man Module aufteilen will.

Ich habe etwa erweiterte tkinter Widgets, die leite ich nicht nur von tkinter ab sondern auch von einer Basisklasse GuiElement. In dieser Basisklasse gibt es auch Methoden, die abfragen, zu welcher Klasse das Widget gehört. Wenn das alles in einem Modul ist, braucht man keinen Kreuzimport. Dann kamen aber die erweiterten ttk Widgets hinzu und die wollte ich in einem anderen Modul haben. Die diese auch mit von GuiElement abgeleitet werden, muß dieses Modul mein Hauptmodul DynTkInter importieren. Da aber GuiElements auch die Klassen der widgets abfragt, muss DynTkInter auch das Ttk Modul importieren. Da die Klassen im Ttk Modul mit von GuiElements abgeleitet werden, muss GuiElements bereits bekannt sein, das heißt dass DynTkInter zuerst importiert sein muss. Aber in DynTkInter muss ich mein Ttk importieren. Aber da ich von nichts im Ttk Modul ableite, gibt es eine Lösung, nämlich dass ich am Schluss des DynTkInter Moduls erst das Ttk Modul importiere. Damit ist das kreuzweise auch in ein Hintereinander aufgelöst und verursacht keine Probleme mit nicht gefundenen Namen. Kreuzweise gegenseitig in zwei Modulen voneinander ableiten dagegen geht nicht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Im vorliegen Fall braucht man aber keinen kreuzweisen import. Das was bei FEMesh in main steht gehört in ein Script, Das Script importiert AbiDraw und AbiDraw importiert FEMesh
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: das wird wieder die selbe Diskussion, wie mit globalen Zuständen, die nicht nur dadurch enstehen, dass irgendwer »global« benutzt.
Du fängst immer wieder mit den globalen Zuständen an. Man kann einen Kreuzimport so vermeiden, indem man die Module nicht zerteilt und schreibt das, was in einem anderen Modul sein sollte in eine Klasse:

Code: Alles auswählen

class Ttk:

    class Button:
        ...
        ...

ttk = Ttk()
Dieses ttk würdest Du jetzt als globalen Zustand ansehen. Denn bei Dir sind ja Objekte globale Zustände.

Aber das ist gleichbedeutend mit:

import Ttk as ttk

Das heißt dann, dass Imports auch globale Zustände sind und man daher Imports vermeiden sollte, oder?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Jede Klasse bekommt eine class_id und die kann man dann abfragen, ohne etwas zu importieren.
Wenn man das aber auch für alle Klassen aus allen importierten Modulen durchziehen will, wird es etwas aufwendig.

Code: Alles auswählen

import AbqDraw
 
class Edge(object):
    def __init__(self):
        self.parent = None
        self.label = None
        self.nodes = None
        self.class_id = 'FEMesh.Edge'
       
    def create_edge(self):
        pass
 
#########################
if __name__ == '__main__':
    print 'within FEMesh.py'
    edge = Edge()
    draw = AbqDraw.drawing()
    draw.draw_edge(edge)
Wenn man aber auch Vererbung dabei braucht kann man class id Klassen machen, wobei dann class id Klassen andere class id Klassen als Basisklassen haben können.

Diese class id Klassen nebst class id Objekten hat man in einem Modul, das man überall importieren kann ohne kreuzweise. Das habe ich auch so gemacht mit Namen für widgets. Ein Name kann ein String sein oder ein Objekt einer Namensklasse.

Das löst natürlich nicht den Fall ganz am Anfang, nämlich dass man Edge zweimal definiert im Mainscript und im Modul.

Aber ich könnte dann den kreuzweisen Import vermeiden.

Jetzt habe ich für mich noch eine viel einfachere Lösung gefunden: ich brauche ja nicht abzufragen, ob eine Widget eine Instanz von einer meiner ttk Klassen ist, sondern es geht genauso ob es eine Instanz der original ttk Klassen ist.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: welches Problem soll denn jetzt diese »class_id« lösen?
Alfons Mittelmeyer hat geschrieben:Jetzt habe ich für mich noch eine viel einfachere Lösung gefunden: ich brauche ja nicht abzufragen, ob eine Widget eine Instanz von einer meiner ttk Klassen ist, sondern es geht genauso ob es eine Instanz der original ttk Klassen ist.
Es geht noch einfacher: gar nicht abfragen, ob ein Objekt Instanz einer bestimmten Klasse ist. Das ist nämlich so gut wie nie nötig und deutet wenn dann auf einen Designfehler hin.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: welches Problem soll denn jetzt diese »class_id« lösen?
Es geht noch einfacher: gar nicht abfragen, ob ein Objekt Instanz einer bestimmten Klasse ist. Das ist nämlich so gut wie nie nötig und deutet wenn dann auf einen Designfehler hin.
Natürlich ist das nötig. In einem Menü biete ich nicht an, dass man da Labels, Buttons, Frames und dergleichen anlegen kann. In einem Notebook oder PanedWindow biete ich kein grid, place oder pack an. Oder sollte ich das?
calo
User
Beiträge: 52
Registriert: Freitag 8. Dezember 2006, 21:35
Wohnort: Stuttgart

@Alle

vielen Dank für die vielen Beiträge und guten Vorschläge.

Eines vorweg: FEMEsh.py ist selbst nur ein Unter-Modul (und kein Mainskript), dass von einem Kernel-Skript geladen wird. Daher habe ich nun zum testen eine FEMesh_test_suite.py geschrieben. Und so funktioniert es, ...und das, ganz ohne Kreuzimporte :D :

Code: Alles auswählen

# FEMesh_test_suite.py

from FEMesh import *
from AbqDraw import *

edge = Edge()           
draw = drawing()
draw.draw_edge(edge)

Code: Alles auswählen

# FEMesh.py

class Edge(object):
    def __init__(self):
        self.parent = 0
        self.label = 0
        self.referenced_elements = []
        self.nodes = 0

    def create_edge(self):
        pass

    def del_edge(self):
        pass

class EdgeArray(list):
    def __init__(self, parent, name=None, edges=[]):
        list.__init__(self, edges)
        self.parent = parent
        self.array_name = name

    def add_edge(self, node_pair, eid):
        pass

    def del_edges(self, edges):
        pass


#######################################
if __name__ == '__main__':
    print 'within FEMesh.py'

Code: Alles auswählen

# AbqDraw.py

import FEMesh 

class drawing(object):
    def __init__(self):
        self.vertices = []
        self.lines = []

    def draw_edge(self, edges):
        if isinstance(edges, FEMesh.Edge):
            print type(edges)
            print FEMesh.Edge

        elif isinstance(edges, FEMesh.EdgeArray):
            print type(edges)
            print FEMesh.Edge



#######################################
if __name__ == '__main__':
    print 'within AbqDraw.py'
Nochmals rechtherzlichen Dank an alle.
Antworten