echte globale Varible

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
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

Hallo,

wie kann ich folgendes Problem lösen.
Im Modul A gibt es zwei Klassen.
Jede Klasse hat ähnliche Funktionen und diese möchte ich auslagern
also in etwa so (funktion _getNextSequence soll ausgelagert werden)

Code: Alles auswählen

from codec import codec

_nextSequence = 0

def _getNextSequence(self):
    _nextSequence += 1
    return _nextSequence

class V0(codec):
    def __init__(self, configFile=None):
        
        codec.__init__(self)
        # self._nextSequence = 0
        
    def __del__(self):
        # terminates class

    # def _getNextSequence(self):
        # self._nextSequence += 1
        # return self._nextSequence
        
        
class V1(codec):
    def __init__(self, configFile=None):
        
        codec.__init__(self)
        # self._nextSequence = 0
        
    def __del__(self):
        # terminates class

    # def _getNextSequence(self):
        # self._nextSequence += 1
        # return self._nextSequence
das führt zum Resultat -> UnboundLocalError: local variable '_nextSequence' referenced before assignment

Wie kann ich das lösen? Der Aufruf der Funktion erfolgt aus der jeweiligen Klasse V0 oder V1.

Danke
Claudia
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

myxin hat geschrieben:das führt zum Resultat -> UnboundLocalError: local variable '_nextSequence' referenced before assignment
Dazu musst du die Variable explizit als global deklarieren. Aber das ist nicht die beste Lösung.
Wie kann ich das lösen? Der Aufruf der Funktion erfolgt aus der jeweiligen Klasse V0 oder V1.
Genau für so etwas gibt es Vererbung.

Code: Alles auswählen

class ACodec(codec):
  def _getNextSequence(self):
      self._nextSequence += 1
      return _nextSequence

# und dann
class V0(ACodec):
  pass
  

# oder falls wirklich "global" gewünscht
class ACodec(codec):
  def _getNextSequence(self):
      ACodec._nextSequence += 1 # das geht auch noch schöner würde jetzt aber zu weit gehen
      return _nextSequence
Außerdem solltest du dir über die Benennung der Methoden Gedanken machen. _getNextSequence stinkt nach Java.

1. ist der Unterstrich überflüssig
2. benutzt man für Funktionsnamen kein CamelCase und
3. Nimmt man statt get* Funktionen eher @property
Zuletzt geändert von Darii am Freitag 13. Januar 2012, 20:58, insgesamt 1-mal geändert.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Man könnte das Problem mit dem `global` Keyword beheben, allerdings halte ich es für wesentlich eleganter ``_getNextSequence = itertools.count().next`` zu nutzen.

Vererbung halte ich hier nicht für sinnvoll, `_getNextSequence` ist nicht spezifisch genug um eine Methode zu rechtfertigen.
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

myxin hat geschrieben:Wie kann ich das lösen? Der Aufruf der Funktion erfolgt aus der jeweiligen Klasse V0 oder V1.
Das sieht nach einer passenden Einsatzmöglichkeit für den count-Iterator aus.

Code: Alles auswählen

import itertools

sequence = itertools.count(start=1)

class V0(codec):
    def __init__(self, configFile=None):
        super(V0, self).__init__(self)
        print(next(sequence))

class V1(codec):
    def __init__(self, configFile=None):
        super(V1, self).__init__(self)
        print(next(sequence))

foo = V0()
bar = V1()
Verwende übrigend nicht __del__ wenn du nicht ganz genau weißt, was du da tust. Das ist kein Destruktor im Sinne von C++ und es ist nicht einmal garantiert, dass diese Methode überhaupt aufgerufen wird.
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Darii hat geschrieben:1. ist der Unterstrich überflüssig
Das sehe ich anders. Diese Methode ist nicht unbedingt als Teil der öffentlichen API gedacht und damit ist ein Unterstrich durchaus angebracht.
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

Erstmal vielen Dank für Eure Anregungen.
Einiges musst ich erstmal im Web suchen um zu verstehen was Ihr meint (Sachen wie CamelCase...) ;-)
Ich versuche es mal mit der Vererbung und dem itertools.

Es ist allerdings eine andere Frage aufgetaucht.

Was bedeutet

Code: Alles auswählen

super(V0, self).__init__(self)
Das habe ich nicht wirklich verstanden.

Danke
Claudia
BlackJack

@myxin: Ich möchte nochmal die Warnung vor `__del__()` bekräftigen. Man kann sich nicht darauf verlassen wann oder ob sie überhaupt aufgerufen wird, und ihr blosses vorhanden sein, kann verhindern, dass Objekte aus dem Speicher entfernt werden. Was dann wieder dazu führt, dass die Methode in diesen Fällen ganz bestimmt nicht aufgerufen wird.

"CamelCase" ist in CamelCase geschrieben. Das macht man in Python konventionell nur bei Klassen. Sonst kleinbuchstaben_mit_unterstrichen. Also statt `_getNextSequence` hiesse es `_get_next_sequence`. Was ein irreführender Name wäre, denn man bekommt ja keine nächste Sequenz sondern aus der Sequenz die nächste Zahl/Nummer.

Mit `super()` kann die nächste Klasse in der Vererbungshierarchie bekommen — was nicht notwendigerweise die Elternklasse sein muss. Man braucht es eigentlich nur wenn man Mehrfachvererbung verwendet und da auch nur wenn es sich nicht nur um Mixin-Klassen handelt. Und es ist IMHO viel zu kompliziert, darum habe ich das noch nie verwendet.
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

Heisst das dann hier

Code: Alles auswählen

class V0(codec):
    def __init__(self, configFile=None):
        super(V0, self).__init__(self)
        print(next(sequence))
das der Konstruktor von der Klasse codec aufgerufen werden soll?

Wenn __del__() vermieden werden soll, was wäre dann die richtige Art einen Dekonstruktor aufzurufen?
del klassenobjekt?

Danke
Claudia
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

myxin hat geschrieben:Was bedeutet

Code: Alles auswählen

super(V0, self).__init__(self)
"There might be dragons!"

Hier bedeutet es den Aufruf der Methode der abgeleiteten Klasse. Als Parameter erhält super typischerweise die Klasse selber und die aktuelle Instanz. In Python 3 kann man diese Parameter auch weglassen.

Spannend wird das bei Mehrfachvererbung.

Code: Alles auswählen

class A(object):
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        super(B, self).__init__()
        print('B')

class C(A):
    def __init__(self):
        super(C, self).__init__()
        print('C')

class D(B, C):
    def __init__(self):
        super(D, self).__init__()
        print('D')

foo = D()
Wie man an der Ausgabe sieht wird die __init__-Methode jeder Klasse nur einmal aufgerufen. Versuch das mal, wenn du anstatt super zu verwenden explizit die Elternklasse angibst.

Es gibt allerdings auch Stimmen, die super für eine nicht so gute Idee halten. Eine Google-Suche nach "super considered harmful" liefert eine Menge Ergebnisse und führt in den Diskussionen in die tiefsten Tiefen von Python. Ich habe versucht das nachzuvollziehen und dann irgendwann das Lesen (und Verstehen) abgebrochen und verwende super einfach.
BlackJack

@myxin: Dort wird *wahrscheinlich* die `__init__()` von `codec` aufgerufen. Das hängt davon ab, welche Objekte sich noch in der Vererbungshierarchie befinden. Lies Dir mal Python's Super is nifty, but you can't use it durch.

Es gibt in Python keine Dekonstruktoren und folglich keine richtige Art sie aufzurufen. ``del name`` ruft auch nichts auf, sondern löscht den gegebenen *Namen* und *nicht* etwa das Objekt was daran gebunden war. Objekte kann man nicht explizit löschen.

@/me: Meine Konsequenz war genau die andere: Das ist höllisch kompliziert und ich hatte noch nie den Fall, dass ich diese ganze Komplexität gebraucht hätte. Also verwende ich `super()` gar nicht.
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

myxin hat geschrieben:Wenn __del__() vermieden werden soll, was wäre dann die richtige Art einen Dekonstruktor aufzurufen?
del klassenobjekt?
Ehrlich gesagt ... gar keine. Zumindest nicht in Python.

del verringert den Referenzzähler. Die __del__-Methode hingegen wird aufgerufen wenn der Referenzzähler auf 0 angekommen ist und der Garbage Collector zuschlägt. Lustig wird das bei zyklischen Abhängigkeiten. Der Garbage Collector kann diese Abhängigkeiten erkennen, hat aber beim Vorhandensein einer __del__-Methode Schwierigkeiten.

Ich frage mich ernsthaft, warum man diesen Sch... implementiert hat.
BlackJack

@/me: Es gibt Situationen da würde man `__del__()` schmerzlich vermissen. Die sind allerdings selten und speziell und man sollte um die Probleme wissen. Typischer Fall sind Klassen im Zusammenhang mit `ctypes`, die etwas repräsentieren was Ressourcen ausserhalb des Python-Prozesses hält, die freigegeben werden sollten wenn das dazugehörige Python-Objekt verschwindet.
myxin
User
Beiträge: 47
Registriert: Dienstag 10. Januar 2012, 16:57

Danke an Alle nochmals.
Die __del__() Funktionen sind verschwunden.
Die links habe ich mir angeschaut und (noch) nicht verstanden, aber ich denke mit der Zeit wird mir das alles ein bisschen verständlicher.

So, jetzt geht es ab auf die Piste - schönes Wochenende wünsche ich Euch.

Gruß
Claudia
Antworten