2016-12-30 2 views
6

Ich habe Datei services.py mit bestimmten Klasse MyCache drin. Alle Instanzen von MyCache sollten ein "Cache" -Feld teilen, also habe ich es statisch gemacht. Um den Cache zu initialisieren, gibt es eine statische Methode, die ihn lädt. Diese Methode wird genau einmal am Anfang der App aufgerufen.initialisieren Feld nur einmal in Python

Das Problem ist, dass, wenn ich services.py aus anderen .py-Datei importieren und Instanz von MyCache erstellen - es druckt, dass Cache leer ist!

Wie kann ich es beheben und "Cache" -Feld von allen Instanzen der Klasse geteilt ignorieren Platz, aus dem sie erstellt werden?

Ich kann nicht verstehen, warum das passiert. Bitte helfen :)

services.py:

class MyCache: 
    cache = {} 

    @staticmethod 
    def initialize(): 
     MyCache.cache = load_from_file() 
     print(len(MyCache.cache)) # prints 3 

    def __init__(self): 
     print(len(MyCache.cache)) # supposed to print 3, because initialize has been called earlier, but prints 0 when called from other_file.py 

main.py:

import services 

if __name__ == '__main__': 
    services.MyCache.initialize() 

other_file.py:

import services 

services.MyCache().foo() # creates instance, prints 0 in MyCache.__init__ 
+0

Was ist 'B' in * Alle Instanzen von B sollte * ein "Cache" teilen? –

+0

Die Namespaces sind unterschiedlich. –

+0

Mein schlechtes (bezogen auf meine gelöschte Antwort). Ich denke, es würde keinen Sinn ergeben, wenn beide nicht anders gedruckt würden. – Dair

Antwort

1
#mycache.py 
def load_from_file(): 
    pass 
    ... 
cache = load_from_file() 

#anotherlib.py 
from mycache import cache 

... 

#main.py 
from anotherlib import ... #(Now the cache is initialized) 
from mycache import cache #(Python looksup the mycache module and doesn't initialize it again) 

Hier sind wir nur mit einem Python-Modul als Singleton. Um mehr darüber zu erfahren, wie Python Caches Module so dass sie nur einmal initialisiert, lesen Sie hier: https://docs.python.org/2/library/sys.html#sys.modules

+0

Ich bekomme 'ImportError: Name kann nicht 'Cache' importiert werden –

+0

@ Jean-FrançoisFabre das ist ein Importfehler, der für mich schwierig ist zu debuggen, ohne zu sehen, was Sie genau gemacht haben. Meine Vermutung ist, dass Sie in Ihrer Mycache.py-Datei keine Modulebenevariable mit dem Namen cache abgelegt haben. – gnicholas

+0

Ja Ich habe eine Modulebene 'cache' erstellt –

0
class MyCache: 
     cache = {} 
     __initialized = False 

     @staticmethod 
     def initialize(): 
      if not MyCache.__initialized: 
       MyCache.cache = load_from_file() 
       MyCache.__initialized = True 

     def __init__(self): 
      print(len(MyCache.cache)) 
+0

, die aus der Datei mehr als einmal geladen wird. –

+0

Dies ist eine gute Lösung (mit Threading.Lock macht es Thread sicher), aber eine Erklärung ist erforderlich. – tdelaney

+0

@ Jean-FrançoisFabre - Ich sehe nur eine Ladung mit '__ initialisiert' (zumindest für Singlethread-Anwendungen). Wie lädt es wieder? – tdelaney

0

Ein Problem ist, dass Sie Module, die Klasse während des Imports mit vor der Ausführung der if __name__ == '__main__: Teil erreicht hat, der die Initialisierung der Fall ist.

Sie können eine verwenden, um den Cache auf Klassenebene bei der ersten Verwendung dynamisch zu initialisieren. Fügen Sie ein Schloss hinzu und es ist auch threadsicher. Sie müssen nicht mehr spezifisch in __main__ initialisieren, was leicht zu vergessen ist, und Sie können es jederzeit von anderen Importeuren verwenden.

import threading 

class MyCache: 
    cache = None 
    _lock = threading.Lock() 

    @classmethod 
    def initialize(cls): 
     with cls._lock: 
      if cls.cache is None: 
       cls.cache = load_from_file() 

    def __init__(self): 
     self.initialize()  
     print(len(MyCache.cache)) 
0

Dies könnte funktionieren - fügen Sie die Klasse Attribut, wenn es nicht bereits existiert eine Metaklasse mit:

foo.py:

def load_stuff(): 
    return {'foo':1, 'bar':2} 

class F(type): 
    def __new__(meta, name, bases, namespace): 
     if 'cache' not in namespace: 
      print('adding cache') 
      namespace['cache'] = load_stuff() 
     return super().__new__(meta, name, bases, namespace) 

class MyCache(metaclass = F): 
    def __init__(self): 
     print(len(MyCache.cache)) 

test.py:

print(__name__) 
import foo 
print(foo.MyCache.cache) 
print('********************') 

tmp.py:


>>> import tmp 
tmp.py 
adding cache 
******************* 
test 
{'foo': 1, 'bar': 2} 
******************** 
>>> tmp.foo.MyCache.cache 
{'foo': 1, 'bar': 2} 
>>> tmp.test.foo.MyCache.cache 
{'foo': 1, 'bar': 2} 
>>> tmp.test.foo.MyCache.cache['x'] = 'x' 
>>> tmp.test.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x'} 
>>> tmp.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x'} 
>>> 
>>> tmp.foo.MyCache.cache is tmp.test.foo.MyCache.cache 
True 
>>> 

>>> import test 
test 
adding cache 
{'foo': 1, 'bar': 2} 
******************** 
>>> test.foo.MyCache.cache 
{'foo': 1, 'bar': 2} 
>>> 
>>> import tmp 
tmp.py 
******************* 
>>> 
>>> tmp.foo.MyCache.cache 
{'foo': 1, 'bar': 2} 
>>> 
>>> tmp.foo.MyCache.cache['x'] = 'x' 
>>> tmp.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x'} 
>>> test.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x'} 
>>> 
>>> z = tmp.foo.MyCache() 
3 
>>> z.cache 
{'foo': 1, 'bar': 2, 'x': 'x'} 
>>> 
>>> z.cache['y'] = 'y' 
>>> z.cache 
{'foo': 1, 'bar': 2, 'x': 'x', 'y': 'y'} 
>>> test.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x', 'y': 'y'} 
>>> tmp.foo.MyCache.cache 
{'foo': 1, 'bar': 2, 'x': 'x', 'y': 'y'} 
>>> 
>>> tmp.foo.MyCache.cache is test.foo.MyCache.cache 
True 

Ich begann zu denken und erkennen, dass das Klassenattribut auch ein Singleton sein könnte, die von dict erbt.

temp.py und test.py - wie oben

foo.py:

def load_stuff(): 
    return [('a', 1), ('b', 2)] 

class Borg: 
    _shared_state = {} 
    def __new__(cls, *a, **k): 
     obj = super().__new__(cls, *a, **k) 
     obj.__dict__ = cls._shared_state 
     return obj 

class Cache(dict, Borg): 
    pass 

class OneCache(metaclass = F): 
    cache = Cache(load_stuff()) 
    def __init__(self): 
     print(len(OneCache.cache)) 

Dann:

>>> import tmp 
>>> tmp.foo.OneCache.cache 
{'a': 1, 'b': 2} 
>>> tmp.test.foo.OneCache.cache 
{'a': 1, 'b': 2} 
>>> z = tmp.foo.OneCache() 
2 
>>> z.cache['r'] = 't' 
>>> z.cache 
{'a': 1, 'b': 2, 'r': 't'} 
>>> tmp.foo.OneCache.cache 
{'a': 1, 'b': 2, 'r': 't'} 
>>> tmp.test.foo.OneCache.cache 
{'a': 1, 'b': 2, 'r': 't'} 
>>> 
>>> tmp.foo.OneCache.cache is tmp.test.foo.OneCache.cache is z.cache 
True 
>>> 
Verwandte Themen