【问题标题】:Initializing class variables only once只初始化一次类变量
【发布时间】:2015-08-23 08:45:06
【问题描述】:
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),它们只能为 db 类初始化一次,db 类的所有其他实例都可以使用它。

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

【问题讨论】:

  • 为什么它们是类属性 global?为什么不提供类方法来初始化它们,或者提供某种工厂方法来动态创建类?
  • 你的类上会有一些“全局”的对象(变量),然后是类中的一个函数,它接受输入并进行计算
  • @JohnRuddell..从哪里调用该函数?
  • 好吧,如果您想立即将值放入 init 中。self.set_gloabals() 和 set_globals 可以查询您的数据库并实际设置值
  • self.set_gloabals() 将为每个类实例调用和处理。我只需要一次。所以我在想是否有其他方法

标签: python oop object pymongo


【解决方案1】:

您始终可以测试这些值是否已先设置;如果您不使用以下简单的线程:

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 而不实例化任何类变量,这将如何工作?
  • @PrannoyMittal:不会的;在这种情况下,您只需要尽早设置这些变量,例如手动触发设置变量。
【解决方案2】:

您可以将其视为具有 memoized 类属性

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

【讨论】:

【解决方案3】:

我会将所有全局类变量放在一个单独的类中,该类可以智能地初始化自身,然后在数据库类的__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 类的构造函数以自定义它的创建。

【讨论】:

    【解决方案4】:

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

    (取决于您在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
    

    【讨论】:

      猜你喜欢
      • 2016-04-29
      • 1970-01-01
      • 2021-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-09
      • 1970-01-01
      • 2013-06-04
      相关资源
      最近更新 更多