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 ids
和global mappings
),只能初始化一次类db
和db
类的所有其他实例可以使用它。
类变量的值必须通过某些函数查找数据库来计算。我该如何组织我的课程?
您可以先测试这些值是否已设置;如果您不使用线程,那么就像这样简单:
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.ids
和self.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