2016-12-30 82 views
6

Mam plik services.py z określoną klasą MyCache w nim. Wszystkie wystąpienia MyCache powinny współdzielić jedno pole "pamięci podręcznej", więc zrobiłem to statycznie. W celu zainicjowania pamięci podręcznej istnieje statyczna metoda, która ją ładuje. Ta metoda jest nazywana dokładnie raz na samym początku aplikacji.Inicjuj pole tylko raz w Pythonie

Problem polega na tym, że po zaimportowaniu pliku services.py z innego pliku .py i utworzeniu instancji MyCache - drukowana jest pamięć podręczna pusta!

Jak mogę to naprawić i utworzyć pole "pamięci podręcznej" współużytkowane przez wszystkie wystąpienia lekceważące klasę miejsca, z którego zostały utworzone?

Nie mogę zrozumieć, dlaczego tak się dzieje. Proszę o pomoc :)

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

Co jest 'B' w * Wszystkie instancje B powinny dzielić jeden "cache" *? –

+0

przestrzenie nazw są różne. –

+0

Mój zły (odnosząc się do usuniętej odpowiedzi). Sądzę, że nie miałoby to sensu, jeśli nie mielibyśmy pracy, gdybyśmy drukowali różne rzeczy. – Dair

Odpowiedz

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) 

Tu są tylko przy użyciu moduł python jako singleton. Aby dowiedzieć się więcej o tym, jak python buforuje moduły więc oni są inicjowane tylko raz przeczytać tutaj: https://docs.python.org/2/library/sys.html#sys.modules

+0

Otrzymuję komunikat "ImportError: nie mogę zaimportować nazwy" cache'' –

+0

@ Jean-FrançoisFabre, który jest błędem importu, który jest dla mnie trudny do debugowania bez zobaczenia, co dokładnie zrobiłeś. Domyślam się, że nie ustawiłeś zmiennej poziomu modułu o nazwie cache w pliku mycache.py. – gnicholas

+0

Tak Stworzyłem poziom modułu 'cache' –

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

ładuje z pliku więcej niż raz. –

+0

To jest dobre rozwiązanie (za pomocą threading.Lock sprawia, że ​​wątek bezpieczny), ale potrzebne jest wyjaśnienie. – tdelaney

+0

@ Jean-FrançoisFabre - Widzę tylko jedno ładowanie chronione przez '__initialized' (przynajmniej dla aplikacji jednowątkowych). Jak ładuje się ponownie? – tdelaney

0

Jednym z problemów jest to, że masz moduły używając klasę podczas importu zanim osiągnie Wykonawczym if __name__ == '__main__: część, która robi inicjalizacji.

Można użyć classmethod do dynamicznego inicjowania pamięci podręcznej na poziomie klasy przy pierwszym użyciu. Dodaj blokadę i jest również bezpieczny dla wątków. Nie musisz już inicjować w szczególności pod numerem __main__, o czym łatwo zapomnieć, i możesz go użyć w dowolnym momencie przez innych importerów.

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

To może działać - dodanie atrybutu class jeżeli nie istnieje używając metaklasa:

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:

print('tmp.py') 
import foo 
print('*******************') 
import test 

>>> 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 

zacząłem myśleć i zorientował się, że atrybut klasy może być również Singleton która dziedziczy z dict.

temp.py i test.py - jak wyżej

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)) 

Następnie:

>>> 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 
>>>