在Python中只初始化一次字段

时间:2016-12-30 21:13:41

标签: python static

我有文件services.py,其中包含某些类MyCache。 MyCache的所有实例都应共享一个“缓存”字段,因此我将其设置为静态。为了初始化缓存,有一个加载它的静态方法。这个方法在app的最开始只调用一次。

问题是当我从其他.py文件导入services.py并创建MyCache实例时 - 它打印出该缓存是空的!

如何修复它并让所有类的实例共享“缓存”字段而忽略它们的创建位置?

我无法理解为什么会这样。请帮助:)

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__

4 个答案:

答案 0 :(得分: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)

这里我们只使用python模块作为单例。要了解有关python如何缓存模块以便仅初始化一次的更多信息,请阅读:https://docs.python.org/2/library/sys.html#sys.modules

答案 1 :(得分:1)

一个问题是,在执行期间,在执行初始化的if __name__ == '__main__:部分之前,您在导入期间使用了类。

您可以使用classmethod在首次使用时动态初始化类级别缓存。添加一个锁,它也是线程安全的。您不再需要在__main__中专门初始化,这很容易忘记,您可以随时由其他进口商使用它。

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

答案 2 :(得分: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)) 

答案 3 :(得分:0)

这可能有用 - 如果使用元类不存在,则添加class属性:

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

我开始思考并意识到class属性也可以是从dict继承的 singleton

temp.py和test.py - 与上面相同

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

然后:

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