Извините, если это АБСОЛЮТНО софомальный вопрос, но мне любопытно, какие есть лучшие практики, и я не могу найти хорошего ответа в Google.
В Python я обычно использую пустой класс в качестве контейнера структуры данных super-catchall (вроде как файл JSON) и добавляю атрибуты по пути:
class DataObj:
"Catch-all data object"
def __init__(self):
pass
def processData(inputs):
data = DataObj()
data.a = 1
data.b = "sym"
data.c = [2,5,2,1]
Это дает мне огромную гибкость, потому что контейнерный объект может по существу хранить что угодно. Поэтому, если возникнут новые требования, я просто добавлю его в качестве другого атрибута в объект DataObj (который я передаю в своем коде).
Однако недавно я (программисты на FP) убедился, что это ужасная практика, потому что очень трудно читать код. Нужно пройти весь код, чтобы выяснить, какие атрибуты на самом деле имеет DataObj.
Вопрос : Как я могу переписать это для большей ремонтопригодности, не жертвуя гибкостью?
Есть ли какие-нибудь идеи из функционального программирования, которые я могу перенять?
Я ищу лучшие практики там.
Примечание . Одна из идей состоит в том, чтобы предварительно инициализировать класс всеми атрибутами, с которыми он может столкнуться, например
class DataObj:
"Catch-all data object"
def __init__(self):
data.a = 0
data.b = ""
data.c = []
def processData(inputs):
data = DataObj()
data.a = 1
data.b = "sym"
data.c = [2,5,2,1]
Это на самом деле хорошая идея? Что если я не знаю, какие у меня атрибуты априори?
Ответы:
Вы не Именно гибкость и является причиной проблемы. Если какой-либо код в любом месте может изменить какие атрибуты у объекта, ремонтопригодность уже разбита на части. В идеале, у каждого класса есть набор атрибутов, которые устанавливаются в камне после
__init__
и одинаковы для каждого экземпляра. Не всегда возможно или разумно, но это должно иметь место, когда у вас нет действительно веских причин избегать этого.Это не очень хорошая идея. Конечно, тогда атрибут есть, но может иметь фиктивное значение или даже действительное значение, которое скрывает код, не присваивающий значение (или код с ошибкой).
AttributeError
это страшно, но получить неправильные результаты хуже. Значения по умолчанию в целом хороши, но чтобы выбрать разумное значение по умолчанию (и решить, что требуется), вам необходимо знать, для чего используется объект.Тогда вы в любом случае облажались и должны использовать вхождение или список вместо жестких кодов имен атрибутов. Но я так понимаю, вы имели в виду «... в то время, когда я пишу контейнерный класс». Тогда ответ таков: «Вы можете редактировать файлы прямо сейчас». Нужен новый атрибут? Добавьте атрибут frigging к классу контейнера. Есть больше кода, использующего этот класс, и ему не нужен этот атрибут? Подумайте о том, чтобы разделить вещи на два отдельных класса (используйте mixins, чтобы остаться СУХИМЫМИ), поэтому сделайте это необязательным, если это имеет смысл.
Если вы боитесь писать повторяющиеся классы контейнеров: применяйте метапрограммирование разумно или используйте,
collections.namedtuple
если вам не нужно мутировать членов после создания (ваши приятели по FP будут рады).источник
Вы всегда можете использовать урок Алекса Мартелли . В твоем случае:
Таким образом, по крайней мере, для читателя ясно, что данные - это просто тупое хранилище данных, и сразу видно, какие значения хранятся под каким именем, поскольку все это происходит в одной строке.
И да, делать это таким образом - иногда хорошая идея.
источник
Я, вероятно, использовал бы второй подход, возможно, используя
None
для указания неверных данных. Это правда, что трудно читать / поддерживать, если вы добавите атрибуты позже. Однако дополнительная информация о назначении этого класса / объекта даст представление о том, почему первая идея - плохой дизайн: где у вас когда-нибудь будет полностью пустой класс без методов или данных по умолчанию? Почему вы не знаете, какие атрибуты у класса?Это возможно , что
processData
может быть лучше в качестве метода (process_data
следовать питон именованию), так как он действует на класс. Учитывая пример, похоже, что это может быть лучше в качестве структуры данных (гдеdict
может быть достаточно).Приведя реальный пример, вы можете рассмотреть вопрос о передаче CodeReview , где они могут помочь в рефакторинге кода.
источник