Initialize a field only once in Python

I have a services.py file with a specific MyCache class. All instances of MyCache must share the same "cache", so I made it static. To initialize the cache, there is a static method that loads it. This method is called exactly once at the very beginning of the application.

The problem is that when I import services.py from another .py file and create an instance of MyCache - it prints that the cache is empty!

How can I fix it and make the cache field common to all instances of the class, ignoring the place from which they are created?

I can’t understand why this is happening. Please, help:)

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__ 
+6
source share
4 answers
 #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) 

Here we just use the python module as singleton. To learn more about how python caches modules, so they only initialize once, read here: https://docs.python.org/2/library/sys.html#sys.modules

+1
source
  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
source

One of the problems is that you have modules that use the class during import before execution reaches the if __name__ == '__main__: that does the initialization.

You can use classmethod to initialize the class level cache on first use. Add a lock as well as thread-safe. You no longer need to initialize it in __main__ , which is easy to forget, and you can use it at any time by other importers.

 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
source

This may work - add a class attribute if it does not already exist using the metaclass:

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 

I started thinking and realized that a class attribute can also be a single that inherits from dict.

temp.py and test.py are the same as above

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

Then:

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

Source: https://habr.com/ru/post/1013655/


All Articles