2016-04-06 4 views
0

Ich habe ein Objekt, das nach der ersten Verwendung zwischengespeichert wird. Ich werde dies mit dem cPickle-Modul tun. Wenn das Modul bereits zwischengespeichert ist, wenn ich versuche, das Objekt das nächste Mal instanziieren (in einem anderen Prozess) möchte ich das zwischengespeicherte Objekt verwenden. meine Grundstruktur folgend:Das Entnehmen des Objekts vom Typ X in der Methode __new__ der Klasse X ruft __init__ beim Zurückgeben des nicht gepackten Objekts auf, warum?

import cPickle 
class Test(object): 
    def __new__(cls, name): 
     if name == 'john': 
      print "using cached object" 
      with open("cp.p", "rb") as f: 
       obj = cPickle.load(f) 
       print "object unpickled" 
       return obj 
     else: 
      print "using new object" 
      return super(Test, cls).__new__(cls, name) 
    def __init__(self, name): 
     print "calling __init__" 
     self.name = name 
     with open("cp.p", "wb") as f: 
      cPickle.dump(self, f) 

Das Problem ist, dass, wenn ich die im Cache gespeicherte Objekt in der __new__ Methode unpickle den __init__ nennt und neu initialisiert alles. Interessanterweise scheint die __init__ nicht nach dem unpickling aufgerufen zu werden, sondern eher wenn das unpickled Objekt zurückgegeben wird. Ich habe eine print-Anweisung hinzugefügt, die das anzeigt ("object unpickled").

Ich habe eine Hacky Abhilfe durch den folgenden Scheck an die __init__ fügen hinzu:

intiailzed = False 
... 
... 
def __init__(self, name): 
    if not self.intialized: 
     self.initialized = True 
     # Rest of the __init__ here 

Neben ein Klassenattribut initialisiert genannt, aber das ist natürlich nicht ideal.

Jeder Einblick, wie die __init__ Methode unterdrückt werden kann (oder warum es überhaupt aufgerufen wird) würde geschätzt.

Edit: basierend auf Feedback hier ist meine neue vorgeschlagene Lösung:

class Test(object): 
    def __new__(cls, name=None): 
     print "calling __new__" 
     if name == 'john': 
      print "using cached object" 
      with open("cp.p", "rb") as f: 
       obj = cPickle.load(f) 
      print "object unpickled" 
      return obj 
     else: 
      print "using new object" 
      obj = super(Test, cls).__new__(cls, name) 
      obj.initialize(name) 
      return obj 

    def __init__(self, name): 
     pass 

    def initialize(self, name): 
     print "calling __init__" 
     self.name = name 
     with open("cp.p", "wb") as f: 
      cPickle.dump(self, f) 
+0

In den Dokumenten [[If __new __() gibt eine Instanz von cls zurück, dann wird die Methode __init __() der neuen Instanz aufgerufen]] (https://docs.python.org/2/reference/datamodel.html #object .__ new__) – unutbu

+1

Pro [Vereinheitlichende Typen und Klassen in Python 2.2] (https://www.python.org/download/releases/2.2.3/descrintro/#__new__), "die unveränderlichen Typen haben einen Dummy" __init__ ', während die veränderbaren Typen einen Dummy' __new__' haben. Wenn Sie nicht einen guten Grund haben (und ich weiß nichts davon), sollten auch Ihre Klassen dem gleichen Muster folgen. – unutbu

+0

Dies war nur eine vereinfachte Verallgemeinerung von dem, was ich eigentlich habe, um meinen Punkt zu erleichtern, die Bedingungen sind sehr unterschiedlich. Danke für den Tipp, obwohl es wahrscheinlich keine schlechte Sache ist, es scheitern zu lassen, wenn man bedenkt, ob meine Bedingung erfüllt ist und die Datei nicht existiert, würde ich wollen, dass sie fehlschlägt. –

Antwort

2

obj = Test(name) funktioniert wie folgt:

obj = Test.__new__(Test, name) 
if isinstance(obj, Test): 
    obj.__init__(name) 

Da das ungebeizte Objekt aus Test.__new__(Test, name) zurückgegeben wird, ist eine Instanz von Test, seine __init__ Methode wird aufgerufen. Es spielt keine Rolle, ob das Objekt von super(Test, cls).__new__ stammt oder unpickling.

Um Probleme wie diese zu vermeiden, sollten Klassen, die __new__ überschreiben, im Allgemeinen vollständig initialisierte Objekte von __new__ zurückgeben und nicht __init__ definieren. Ihre Unterklassen sollten ebenfalls dieser Regel folgen.

+0

Ich habe eine Lösung basierend auf Ihrem Vorschlag hinzugefügt, können Sie einen Blick darauf werfen und mir einen Feed geben. Es scheint zu funktionieren, wie ich es will. –

+0

@ J.Doe: Ja, das funktioniert, obwohl ich 'initialize' nicht sagen würde," __init__ "aufrufen. – user2357112

+0

Vielen Dank für die Hilfe, und ja, ich habe nur hastig alles in 'initialize' kopiert, ich hätte diese Druckanweisungen normalerweise nicht. –

Verwandte Themen