Разве плохо хранить все экземпляры класса в поле класса? - PullRequest
2 голосов
/ 28 января 2011

Мне было интересно, есть ли что-то не так (с точки зрения ООП) в том, чтобы сделать что-то вроде этого:

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]

РЕДАКТИРОВАТЬ : это настоящий фрагмент кода IЯ использую прямо сейчас

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.Будет ли разумнее иметь глобальную переменную вместо поля класса?Я мог бы использовать здесь неправильный подход, возможно, этот материал даже не должен быть в методе, но это имело смысл для меня (я новичок в ООП)

Ответы [ 7 ]

9 голосов
/ 28 января 2011

Да. Это плохо. Он объединяет экземпляр с коллекцией экземпляров.

Коллекции - это одно.

Собранные экземпляры не связаны.

Кроме того, переменные уровня класса, которые обновляются, смущают некоторых из нас. Да, в конечном итоге мы можем рассуждать о том, что происходит, но стандартное ожидание ™ заключается в том, что изменение состояния применяется к объектам, а не к классам.


 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 - это просто метод сбора задач, а не отдельная задача. Вам не нужны глобалы.

Вам необходимо провести рефакторинг.

4 голосов
/ 28 января 2011

Не думаю, что с этим что-то есть неправильно , но я не понимаю, насколько это будет разумно. Зачем вам нужно хранить глобальную переменную (в классе, во всех местах), которая содержит ссылки на все экземпляры? Клиент мог бы так же легко реализовать это сам, если бы он просто вел список своих экземпляров. В общем, это кажется немного хакерским и ненужным, поэтому я бы порекомендовал вам этого не делать.

Если вы более конкретно о том, что вы пытаетесь сделать, возможно, мы сможем найти лучшее решение.

1 голос
/ 28 января 2011

Зачем вам это нужно?

Есть несколько проблем с этим кодом. Во-первых, вы должны позаботиться об удалении экземпляров - всегда будет ссылка на каждый Foobar экземпляр, оставленный в Foobar.foobars, поэтому сборщик мусора никогда не будет собирать их. Вторая проблема заключается в том, что он не будет работать с copy и pickle.

Но кроме технических проблем, это похоже на неправильный дизайн. Цель экземпляров объекта - скрыть состояние, и вы заставляете их видеть друг друга.

1 голос
/ 28 января 2011

Это НЕ связно, а также не очень функционально, вы хотите стремиться отодвинуть ваши объекты как можно дальше от мышления «корзины данных».Коллекция статических объектов на самом деле ничего не даст вам, вам нужно подумать, ПОЧЕМУ вам нужны все объекты в коллекции, и подумать о создании второго класса, в обязанности которого входит управление всеми запросами Foobar в системе и их использование.

0 голосов
/ 06 февраля 2014

Мне требовалось несколько сред 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')
0 голосов
/ 28 января 2011

Никто не упомянул о потенциальной проблеме, которая могла бы возникнуть, если бы вы позже вывели подкласс из Foobar, что может произойти, если функция базового класса __init__() вызывается из __init__() производного класса. В частности, хотите ли вы, чтобы все экземпляры подкласса располагались в одном месте с экземплярами базового класса - что, конечно, зависит от того, почему вы это делаете.

Это решаемая проблема, но кое-что нужно рассмотреть и, возможно, написать для кода в базовом классе.

0 голосов
/ 28 января 2011

С точки зрения ООП в этом нет ничего плохого.Класс является экземпляром метакласса, и любой экземпляр может содержать в нем данные любого типа.

Однако, с точки зрения эффективности, если вы в конечном итоге не очистите диктовку foobars надолго работающая программа на Python, у вас есть потенциальная утечка памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...