只需初始化类变量一次

6
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

我需要一些类变量(全局ID全局映射),这些变量只能为db类初始化一次,而db类的所有其他实例都可以使用它。

类变量的值必须通过某个函数计算,该函数查找数据库。我应该如何构建我的类?


为什么它们是类属性 global?为什么不提供一个类方法来初始化它们,或者一些动态创建类的工厂方法呢? - jonrsharpe
你会在类中拥有一些被称为“全局”的对象(变量),然后在类内部有一个函数,该函数接受输入并进行计算。 - John Ruddell
@JohnRuddell..在哪里调用那个函数? - Prannoy Mittal
如果你想立即设置这些值,可以在 init(初始化)方法中进行操作。使用 self.set_gloabals() 方法并且该方法可以查询你的数据库并实际设置这些值。 - John Ruddell
self.set_globals()将被每个类实例调用和处理。我只需要它一次。所以我在想是否有其他方法。 - Prannoy Mittal
在你的init中添加一个检查。if not self.global_mappings: 这样它只会被实例化一次 :) - John Ruddell
4个回答

3

您可以始终先测试这些值是否已设置;如果您不使用线程,则可以简单地执行以下操作:

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

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

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


如果我在不实例化任何类变量的情况下访问类变量global_ids和global_mappings,这将如何工作? - Prannoy Mittal
@PrannoyMittal:不会的;在那种情况下,你只需要提前设置这些变量,例如手动触发设置变量。 - Martijn Pieters

1

我会将所有全局类变量放在一个独立的类中,该类会自行初始化,并在数据库类的__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引用全局数据。如果需要,还可以通过向全局类的构造函数传递参数来自定义其创建。


0
你可以把它看作是一个具有记忆的类属性。
虽然 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__中来实现这一点,这会导致未来的调用直接访问它,而不是访问类属性:
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

我认为现在在functools模块中有一个@cached_property装饰器实现了这个功能。 - smac89

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接