проблема
Я работаю над проектом Python, основным классом которого является « Объект Бога ». Существует так много чертовых атрибутов и методов!
Я хочу изменить класс.
Уже…
Для первого шага я хочу сделать что-то относительно простое; но когда я попробовал самый простой подход, он сломал некоторые тесты и существующие примеры.
По сути, у класса есть довольно длинный список атрибутов, но я могу четко просмотреть их и подумать: «Эти 5 атрибутов связаны ... Эти 8 также связаны ... а затем есть остальные».
GetAttr
Я просто хотел сгруппировать связанные атрибуты в вспомогательный класс, похожий на dict. У меня было ощущение, __getattr__
что это идеально подходит для работы. Поэтому я переместил атрибуты в отдельный класс и, конечно же, __getattr__
отлично справился со своей магией ...
Во первых .
Но затем я попытался запустить один из примеров. Подкласс примера пытается установить один из этих атрибутов напрямую (на уровне класса ). Но так как атрибут больше не был «физически расположен» в родительском классе, я получил сообщение о том, что атрибут не существует.
@имущество
Я тогда прочитал о @property
декораторе. Но потом я также прочитал, что это создает проблемы для подклассов, которые хотят делать, self.x = blah
когда x
это свойство родительского класса.
Желаемый
- Пусть весь клиентский код продолжает работать
self.whatever
, даже еслиwhatever
свойство родителя не «физически расположено» в самом классе (или экземпляре). - Сгруппируйте связанные атрибуты в похожие на dict контейнеры.
- Уменьшите чрезмерную шумность кода в основном классе.
Например, я не просто хочу изменить это:
larry = 2
curly = 'abcd'
moe = self.doh()
В это:
larry = something_else('larry')
curly = something_else('curly')
moe = yet_another_thing.moe()
... потому что это все еще шумно. Хотя это успешно превращает простой атрибут во что-то, что может управлять данными, оригинал имел 3 переменные, а измененная версия все еще имеет 3 переменные.
Тем не менее, я был бы в порядке с чем-то вроде этого:
stooges = Stooges()
И если поиск self.larry
не удастся, что-то проверит stooges
и посмотрит, есть ли larry
там. (Но это также должно работать, если подкласс пытается сделать это larry = 'blah'
на уровне класса.)
Резюме
- Хотите заменить связанные группы атрибутов в родительском классе одним атрибутом, который хранит все данные в другом месте
- Хотите работать с существующим клиентским кодом, который использует (например)
larry = 'blah'
на уровне класса - Хотите продолжать разрешать подклассам расширять, переопределять и изменять эти измененные атрибуты, не зная, что что-то изменилось
Это возможно? Или я лаю не на том дереве?
источник
Ответы:
Написав, а затем рефакторинг питона "объект Бога", я сочувствую. Я разбил оригинальный объект на подразделы, основанные на методах. Например, оригинал выглядел как этот псевдокод:
Метод материала - это автономная «единица» работы. Я перенес его в новый класс, который создается в оригинале. Это также вытянуло необходимые свойства. Некоторые использовались только подклассом и могли двигаться прямо через дорогу. Другие были поделены, и были переведены в общий класс.
«Объект Бога» создает новую копию общего класса при запуске, и каждый из новых подклассов принимает указатель как часть их метода init. Например, вот урезанная версия почтовой программы:
Он создается один раз и используется разными классами, которым нужны возможности почтовой рассылки.
Поэтому для вас создайте класс
larry
с нужными вам свойствами и методами. Везде, где клиент говоритlarry = blah
заменить егоlarryObj.larry = blah
. Это переносит вещи в подпроекты, не нарушая текущий интерфейс.Единственное, что нужно сделать, это искать «единицы работы». Если вы собираетесь превратить часть «Объекта Бога» в его собственный метод, сделайте это . Но поместите метод вне этого. Это заставляет вас создавать интерфейс между компонентами.
Создание этой основы позволяет всему остальному следовать ей. Например, фрагмент вспомогательного объекта, демонстрирующий, как он взаимодействует с почтовой программой:
Сконцентрируйтесь на наименьшей отдельной возможной единице работы и уберите ее. Это проще сделать и позволяет быстро поиграть с настройкой. Не смотрите на свойства для перемещения вещей, они являются вспомогательными для задач , выполняемых с ними в большинстве случаев. Все, что осталось после того, как вы разберетесь с методами, вероятно, должно остаться в исходном объекте, так как он является частью общего состояния.
Но теперь новые объекты должны принимать нужные им свойства в качестве переменных init, не затрагивая свойство объектов вызывающего. Затем они возвращают любые необходимые значения, которые вызывающий может использовать для обновления общих свойств по мере необходимости. Это помогает отделить объекты и делает систему более надежной.
источник