将类的所有实例存储在类字段中是不是很糟糕?

时间:2011-01-28 17:38:11

标签: python oop class field

我想知道在做这样的事情时是否有任何错误(从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的新手)

7 个答案:

答案 0 :(得分:9)

是。这不好。它将实例与实例集合混淆。

收藏是一回事。

收集的实例是无关的。

此外,更新的类级变量会使我们中的一些人感到困惑。是的,我们最终可以推断正在发生的事情,但标准期望™是状态变化适用于对象,而不是类。


 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只是一个任务集合的方法,而不是单个任务。你不需要全局变量。

你需要重构。

答案 1 :(得分:4)

我不认为这有什么错误,但我真的不明白这是多么明智。为什么需要保留一个包含所有实例引用的全局变量(在所有地方的类中)?如果客户只保留他的实例列表,客户可以轻松地自己实现。总而言之,它看起来有点苛刻和不必要,所以我建议你不要这样做。

如果你对你正在尝试做的事情更具体,也许我们可以找到更好的解决方案。

答案 2 :(得分:1)

这不是一致的,也不是很实用,你想尽可能地让你的对象远离'数据桶'的思维模式。静态对象集合不会真正获得任何东西,您需要考虑为什么需要集合中的所有对象并考虑创建第二个类,其职责是管理和查询系统中的所有Foobar。

答案 3 :(得分:1)

你为什么要这样做?

此代码存在一些问题。第一个是你必须要处理删除实例 - 总会有Foobar中留下的每个Foobar.foobars实例的引用,所以垃圾收集器永远不会垃圾收集它们。第二个问题是它不适用于copypickle

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

答案 4 :(得分:0)

从OOP的角度来看,它没有任何问题。类是元类的实例,任何实例都可以包含任何类型的数据。

但是,从效率的角度来看,如果你不想在长期运行的Python程序中清理foobars dict,那么你可能会有内存泄漏。

答案 5 :(得分:0)

如果稍后从Foobar派生一个子类,如果从派生类调用基类__init__()函数__init__()可能会发生这种情况,那么没有人提到过这可能会遇到的潜在问题。{{ 1}}。具体而言,您是否希望将所有子类实例与基类的实例放在同一位置 - 这当然取决于您执行此操作的原因。

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

答案 6 :(得分:0)

我在应用引擎应用中需要多个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')