只初始化类变量一次


class db: 
      global_ids = {}
      global_mappings = {}
      def __init__:
          db_client     = sth sth #clinet to mongo db
          db_connection = sth sth #connection to mongo db 
          db_coll        = sth sth

我需要一些类变量(global idsglobal mappings),只能初始化一次类dbdb类的所有其他实例可以使用它。

类变量的值必须通过某些函数查找数据库来计算。我该如何组织我的课程?

您可以先测试这些值是否已设置;如果您不使用线程,那么就像这样简单:

class db: 
      global_ids = {}
      global_mappings = {}
      def __init__(self):
          self.db_client     = sth sth #clinet to mongo db
          self.db_connection = sth sth #connection to mongo db 
          self.db_coll        = sth sth
          if not db.global_ids:
              # set key-value pairs on db.global_ids
          if not db.global_mappings:
              # set key-value pairs on db.global_mappings

这样你就可以推迟设置这些类的属性,直到你第一次创建一个类的实例。

当然,当定义类时,您也可以选择设置相同的值。

我将把所有全局类变量放在一个单独的类中,这个类可以聪明地初始化自己,然后在数据库类的__init__()方法中创建它的实例,如下面的代码所示:

class DBGlobals(object):
    initialized = False
    ids = {}
    mappings = {}
    def __init__(self):
        if not self.initialized:
            print('initializing globals')
            self.ids = {'ans': 41}          # initialize as needed
            self.mappings = {'v1': 'v2'}    # initialize as needed
            self.initialized = True
class DB(object):
    dbglobals = DBGlobals()
    def __init__(self):
        db_client     = sth sth #clinet to mongo db
        db_connection = sth sth #connection to mongo db
        db_coll       = sth sth

可以分别使用self.dbglobals.idsself.dbglobals.mappings在类方法中引用全局数据。如果需要,还可以将参数传递给globals类的构造函数以自定义它的创建。

你可以把它想象成有一个熟记的类属性

虽然classproperty不是python内置的,但它很容易实现:

class classproperty(object):
    """ @classmethod+@property """
    def __init__(self, f):
        self.f = classmethod(f)
    def __get__(self, *a):
        return self.f.__get__(*a)()

可以这样使用:

class A:
    @classproperty
    def client1(cls):
        print("creating a client1 for %s..." % cls)
        return 0
print(A.client1)
print(A.client1)
print(A().client1)
Output:
creating a client1 for <class '__main__.A'>...
0
creating a client1 for <class '__main__.A'>...
0
creating a client1 for <class '__main__.A'>...
0

现在剩下的是记忆它,所以修饰函数只被调用一次,以后调用返回第一次计算的值。这可以通过将值"注入"到A.__dict__来实现,这将导致以后的调用直接访问它,而不是classproperty:

class memoized_classproperty(object):
    """ @classmethod+@property """
    def __init__(self, f):
        self.f = classmethod(f)
    def __get__(self, instance, owner):
        # get the value:
        value = self.f.__get__(instance, owner)()
        # inject the value into class's __dict__ before returning:
        attr = self.f.__func__.__name__
        setattr(owner, attr, value)
        return value
class A:
    @memoized_classproperty
    def client2(cls):
        print("creating a client2 for %s..." % cls)
        return 0
print(A.client2)
print(A.client2)
print(A().client2)
Output:
creating a client2 for <class '__main__.A'>...
0
0
0

基于布尔测试短路的替代方案

(取决于您在setUpClass中放入的内容,您可以轻松地将行为子类化)

class db1:
    global_ids = {}
    global_mappings = {}
    @classmethod
    def setUpClass(cls):
        print("...running setUpClass on %s..." % (cls.__name__))
        cls.global_ids = {"port": 8000}
        cls.global_mappings: {"global": "mappings"}
    def __init__(self, message):
        print("%s.__init__(%s)" % (self.__class__.__name__, message))
        # setUpClass only gets called if global_ids is Falsy
        self.global_ids or self.setUpClass()
        print("  port:%s" % (self.global_ids["port"]))
class db2(db1):
    # global_ids are recomputed for db2
    global_ids = {}
class db3(db1):
    # but db3 and db1 share the same settings
    pass
db1("first")
db1("second - reuse first")
db2("third  - recompute for this class")
db3("fourth - reuse first")
输出:

db1.__init__(first)
...running setUpClass on db1...
  port:8000
db1.__init__(second - reuse first)
  port:8000
db2.__init__(third  - recompute for this class)
...running setUpClass on db2...
  port:8000
db3.__init__(fourth - reuse first)
  port:8000

最新更新