【问题标题】:Is it bad to store all instances of a class in a class field?将一个类的所有实例存储在一个类字段中是不是很糟糕?
【发布时间】:2011-06-17 09:47:54
【问题描述】:

我想知道做这样的事情是否有什么问题(从 OOP 的角度来看):

class Foobar:
    foobars = {}
    def __init__(self, name, something):
        self.name = name
        self.something = something

        Foobar.foobars[name] = self

Foobar('first', 42)
Foobar('second', 77)

for name in Foobar.foobars:
    print name, Foobar.foobars[name]

编辑:这是我现在正在使用的实际代码

from threading import Event
class Task:
    ADDED, WAITING_FOR_DEPS, READY, IN_EXECUTION, DONE = range(5)
    tasks = {}
    def __init__(self, name, dep_names, job, ins, outs, uptodate, where):
        self.name = name
        self.dep_names = [dep_names] if isinstance(dep_names, str) else dep_names
        self.job = job
        self.where = where
        self.done = Event()
        self.status = Task.ADDED
        self.jobs = []
        # other stuff...
        Task.tasks[name] = self
    def set_done(self):
        self.done.set()
        self.status = Task.DONE
    def wait_for_deps(self):
        self.status = Task.WAITING_FOR_DEPS
        for dep_name in self.dep_names:
            Task.tasks[dep_name].done.wait()
        self.status = Task.READY
    def add_jobs_to_queues(self):
        jobs = self.jobs
        # a lot of stuff I trimmed here
        for w in self.where: Queue.queues[w].put(jobs)
        self.status = Task.IN_EXECUTION
    def wait_for_jobs(self):
        for j in self.jobs: j.wait()
    #[...]

如您所见,我需要访问包含所有实例的字典 wait_for_deps 方法。有一个全局变量会更有意义吗 而不是类字段?我可能在这里使用了错误的方法,也许是 东西甚至不应该在方法中,但对我来说很有意义(我是 OOP 的新手)

【问题讨论】:

  • 如果你决定继续这样做,我建议你使用self.foobarstype(self).foobars 这样类就不会被硬编码。后者需要新型类(即从对象继承的类)。

标签: python oop class field


【解决方案1】:

我需要在一个应用引擎应用程序中使用多个 Jinja 环境:

class JinjaEnv(object):
    """ Jinja environment / loader instance per env_name """

    _env_lock = threading.Lock()
    with _env_lock:
        _jinja_envs = dict()                                # instances of this class

    def __init__(self, env_name):

        self.jinja_loader = .....                           # init jinja loader
        self.client_cache = memcache.Client()
        self.jinja_bcc = MemcachedBytecodeCache(self.client_cache, prefix='jinja2/bcc_%s/' % env_name, timeout=3600)
        self.jinja_env = self.jinja_loader(self.jinja_bcc, env_name)

    @classmethod
    def get_env(cls, env_name):

        with cls._env_lock:
            if env_name not in cls._jinja_envs:
                cls._jinja_envs[env_name] = JinjaEnv(env_name)   # new env
            return cls._jinja_envs[env_name].jinja_env

    @classmethod
    def flush_env(cls, env_name):

        with cls._env_lock:
            if env_name not in cls._jinja_envs:
                self = cls._jinja_envs[env_name] = JinjaEnv(env_name)   # new env
            else:
                self = cls._jinja_envs[env_name]
                self.client_cache.flush_all()
                self.jinja_env = self.jinja_loader(self.jinja_bcc, env_name)
            return self.jinja_env

像这样使用:

template = JinjaEnv.get_env('example_env').get_template('example_template')

【讨论】:

    【解决方案2】:

    是的。这不好。它将实例与实例集合混为一谈。

    收藏是一回事。

    收集的实例是不相关的。

    此外,更新的类级变量让我们中的一些人感到困惑。是的,我们最终可以推断发生了什么,但标准期望™ 是状态更改适用于对象,而不是类。


     class Foobar_Collection( dict ):
         def __init__( self, *arg, **kw ):
             super( Foobar_Collection, self ).__init__( *arg, **kw ):
         def foobar( self, *arg, **kw ):
             fb= Foobar( *arg, **kw )
             self[fb.name]= fb
             return fb
    
     class Foobar( object ):
         def __init__( self, name, something )
             self.name= name
             self.something= something
    
    fc= Foobar_Collection()
    fc.foobar( 'first', 42 )
    fc.foobar( 'second', 77 ) 
    
    for name in fc:
        print name, fc[name]
    

    这更典型。


    在您的示例中,wait_for_deps 只是任务集合的一种方法,而不是单个任务。你不需要全局变量。

    你需要重构。

    【讨论】:

    • 我发布了我正在使用的实际代码的 sn-p,我可能应该首先这样做。正如Refe Kettler 建议的那样,我会尝试使用您的方法。谢谢你的帮助。如果在看到实际代码后您有更多建议,我很乐意听到。
    【解决方案3】:

    没有人提到如果您以后从Foobar 派生一个子类可能会出现的潜在问题,如果从派生类的__init__() 调用基类__init__() 函数可能会发生这种情况。具体来说,您是否希望将所有子类实例与基类实例放在同一个位置——这当然取决于您这样做的原因。

    这是一个可以解决的问题,但需要在基类中预先考虑,或许还需要编写代码。

    【讨论】:

      【解决方案4】:

      从 OOP 的角度来看,它没有任何问题。类是元类的一个实例,任何实例都可以在其中保存任何类型的数据。

      但是,从效率的角度来看,如果您最终没有清理长时间运行的 Python 程序上的 foobars dict,那么您就有潜在的内存泄漏。

      【讨论】:

        【解决方案5】:

        你为什么要这样做?

        这段代码有几个问题。首先是您必须注意删除实例——Foobar.foobars 中的每个Foobar 实例总会有一个引用,因此垃圾收集器永远不会对它们进行垃圾收集。第二个问题是它不适用于copypickle

        但除了技术问题之外,感觉就像一个错误的设计。对象实例的目的是隐藏状态,让它们互相看到。

        【讨论】:

          【解决方案6】:

          这不是连贯的,也不是很实用,你想努力让你的对象尽可能远离“数据桶”的思维方式。静态对象集合不会真正为您带来任何好处,您需要考虑为什么需要集合中的所有对象,并考虑创建第二个类,其职责是管理和查询系统中的所有 Foobar。

          【讨论】:

            【解决方案7】:

            我不认为这有什么错误,但我真的不明白这有什么意义。为什么需要保留一个全局变量(在所有位置的类中)保存对所有实例的引用?如果客户只保留他的实例列表,他自己也可以轻松地实现这一点。总而言之,这似乎有点hackish和不必要的,所以我建议你不要这样做。

            如果您更具体地了解您想要做什么,也许我们可以找到更好的解决方案。

            【讨论】:

            • “看起来有点老套和不必要”。我认为您可以从中减少“一点”。
            • 我从我正在使用的代码中发布了一个 sn-p,也许现在更清楚我要做什么了。
            • @molok 看到代码后,似乎 S. Lott 所写的示例最适合您:任务队列的特殊类
            猜你喜欢
            • 2012-02-17
            • 2013-04-17
            • 2019-09-25
            • 2022-07-21
            • 1970-01-01
            • 2015-10-29
            • 2020-01-29
            • 2011-08-28
            • 2015-08-19
            相关资源
            最近更新 更多