Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize field only once in Python

Tags:

python

static

I have file services.py with certain class MyCache in it. All instances of MyCache should share one "cache" field, so I made it static. In order to initialize cache there is static method that loads it. This method is called exactly once at the very start of the app.

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

How can I fix it and make "cache" field shared by all instances of the class disregard of place from which they are created?

I can't get why this happens. 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__
like image 863
Vitaly Trifanov Avatar asked Dec 30 '16 21:12

Vitaly Trifanov


People also ask

How to initialize a list in Python?

The values of the list are called items or elements. Python list can contain the same value multiple times, unlike set. Each occurrence is considered a different item. Initialize a list using square brackets. Initialize using list () function. You can use square brackets to initialize an empty list in Python.

When should I call Python module initialization?

In an application embedding Python, this should be called before using any other Python/C API functions; see Before Python Initialization for the few exceptions. This initializes the table of loaded modules ( sys.modules ), and creates the fundamental modules builtins, __main__ and sys. It also initializes the module search path ( sys.path ).

What is Py_initialize () in Python?

See also Python Initialization Configuration. In an application embedding Python, the Py_Initialize () function must be called before using any other Python/C API functions; with the exception of a few functions and the global configuration variables. The following functions can be safely called before Python is initialized:

How to customize the fields in Python data class?

To customize the fields in the python data class, we will have to import fields from the data classes module. Now we can customize the fields. There are six optional parameters that can be set to customize the field properties. Let’s take a look at each parameter.


2 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 are just using a python module as a singleton. To learn more about how python caches modules so they are only initialized once, read here: https://docs.python.org/2/library/sys.html#sys.modules

like image 191
gnicholas Avatar answered Sep 27 '22 18:09

gnicholas


One problem is that you have modules using the class during import before execution has reached the if __name__ == '__main__: part that does the initialization.

You can use a classmethod to initialize the class-level cache dynamically on first use. Add a lock and it is also thread-safe. You no longer need to initialize specifically 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))
like image 42
tdelaney Avatar answered Sep 27 '22 17:09

tdelaney