Хотя у меня есть большой опыт использования Python, я обнаружил, что иногда довольно сложно определить, следует ли помещать в класс соответствующие функции и атрибуты. Точнее, у меня есть функция, которая использует атрибуты класса, а следующие функции последовательно используют возвращаемое значение предыдущей функции. Например, Функция 1 --> Функция 2 --> Функция 3 и так далее, причем каждая функция что-то возвращает.
Я хочу понять, имеет ли смысл использовать класс в подобных ситуациях, поскольку это обычное явление для меня. Я хочу убедиться, что объект (таблица продаж) создан логичным и чистым способом.
Пока я создал простой класс с некоторыми атрибутами и методами экземпляра. Я не уверен, как еще я могу это сделать. Я просмотрел множество сообщений о стеках, статей и многих других ресурсов. Я считаю, что у меня есть хорошее понимание цели класса, но в меньшей степени, когда его целесообразно использовать.
Чтобы было ясно, я не прошу помощи по самим функциям или их логике (хотя я ценю любые предложения!). Я просто хочу знать, подходит ли использование класса. Я не включал какой-либо код в функции, так как не думаю, что их логика имеет отношение к моему вопросу (при необходимости могу добавить!)
class SalesTable:
def __init__(self, banner, start_year, start_month, end_year, end_month):
"""These attributes act as filters when searching for the relevant data."""
self.banner = banner
self.start_year = start_year
self.start_month = start_month
if not end_year:
self.end_year = start_year
else:
self.end_year = end_year
if not end_month:
self.end_month = start_month
else:
self.end_month = end_month
def sales_periods(self):
"""Will create a dict with a key as the year and each year will have a list of months as the value. The
stated attributes are used ONLY here as filters to determine what years and months are included"""
pass
def find_sales_period_csv(self):
"""Using the dictionary returned from the function above, will search through the relevant directories and
subdirectories to find all the paths for individual csvs where the sales data is stored as determined by the
value in the dictionary and store the paths in a list"""
pass
def csv_to_df(self):
"""Using the list returned from the function above, will take each csv path in the list and convert them into a
dataframe and store those dateframes in another list"""
pass
def combine_dfs(self):
"""Using the list return from the function above, will concatenate all dfs into a single dataframe"""
def check_data(self):
"""Maybe do some checking here to ensure all relevant data concatenated properly (i.e total row count etc.)"""
В идеале мне нравится возвращать таблицу продаж через последнюю функцию (combine_dfs), следуя последовательности функций. Я могу выполнить эту задачу довольно легко, однако я не уверен, что это лучший способ структурировать мой сценарий или если это логически имеет смысл, несмотря на то, что он работает так, как я хочу.
И, если после исключения всех «обычных» функций из вашего класса, если у вас не останется ничего, кроме __init__ и еще одного метода, велика вероятность, что другой метод должен быть также просто функцией, которая принимает дополнительные параметры из текущего состояния. несуществующий метод __init__, и класс должен быть утилизирован.
Да, только функция sales_periods принимает атрибуты. Поэтому было бы правильно сказать, что имеет смысл использовать класс только тогда, когда несколько методов используют указанные атрибуты экземпляра. Спасибо за ваше объяснение, это было действительно ясно!






Если вам кажется, что куча данных и функций живут вместе, то есть вы обычно обращаетесь к ним обоим одновременно, то у вас есть веские основания полагать, что у вас есть объект в ваших руках.
Еще одна веская причина — если у объекта есть естественное имя. Странно, я знаю, но это действительно полезный руководящий принцип.
Чтение ТВЕРДЫЙ также может дать вам пищу для размышлений.
Спасибо за совет! Это «стол», поэтому я подумал, что он кажется естественным, хотя он кажется более абстрактным, чем реальным. Пойду читать, спасибо за ссылку!
В идеале есть два основных варианта использования class:
1) Во избежание повторения. Если вы создаете один и тот же объект несколько раз, чем он должен быть в классе.
2) Группировать предметы. Читать чей-то код намного проще, если все связанные функции и атрибуты сгруппированы вместе. Это также упрощает ремонтопригодность и портативность.
Обычно методы вызывают друг друга внутри класса, поскольку в идеале методы не должны быть длиннее 30 строк (хотя разные группы имеют разные стандарты). Если вы вызываете методы только внутри класса, то этот метод должен быть private, и вы должны добавить __ (два символа подчеркивания) перед этим методом.
Таким образом, объект таблицы продаж будет создан только один раз, поэтому, основываясь на номере 1, я бы предположил, что класс будет неуместным. Однако мои функции и атрибуты связаны тем, что вам нужны атрибуты для первой функции и первой функции для второй и так далее, поэтому я думаю, что номер 2 применим. Это просто вопрос суждения? Я также буду помнить о подчеркиваниях! Спасибо!
@OskiTuranoglu Как правило, да, это вопрос суждения. Если применимо одно из двух применений, вам, вероятно, следует использовать класс. Вы обнаружите, что с помощью кода инкапсулирующий вам будет легче исправлять ошибки и вносить изменения, поскольку вся логика выполняется с помощью сокрытие информации.
По моему личному опыту, я всегда стараюсь размещать свой код в классах. Есть очень мало случаев, когда я не использую класс.
Единственное исключение, если я делаю быстрый/короткий скрипт, чтобы сделать что-то простое, например, переименовать все файлы в каталоге.
Люди, плохо знакомые с ООП, склонны создавать слишком много классов (я знаю, что делал это вначале). Одной из проблем является читабельность кода: когда в коде используется пользовательский класс, часто необходимо прочитать определение класса, чтобы понять, что этот класс должен делать. Если в коде используются только встроенные типы, разобраться обычно проще. Кроме того, сложное внутреннее состояние, которое является естественной особенностью классов, часто является источником незаметных ошибок и затрудняет анализ кода.
Понимаю. Таким образом, мне на самом деле нужно создать 4 таблицы, все разные с точки зрения данных, но в конечном итоге все они должны быть фреймами данных по отдельности (тот же вывод, что и выше). Я думал, что создание отдельного модуля для каждой таблицы, и каждый модуль будет иметь класс для создания экземпляра одного из них, упростит внесение изменений и переносимость. Не могли бы вы сказать, что просто сделать 4 разных модуля без классов будет правильным решением?
Эта книга весьма полезен
Каждый из ваших методов выше выглядит так, как будто они относятся к классу. Допустим, вы определили кучу функций вне класса и передавали один и тот же набор из десяти переменных в качестве аргументов каждой из них. Это было бы знаком того, что они должны быть в классе. Доступ и изменение слишком большого количества переменных и передача их другим функциям в качестве аргументов вместо использования их в качестве атрибутов класса, которые изменяются внутри каждого из методов, были бы признаком того, что вы не смогли воспользоваться одним из преимуществ классов. В той книге я помню раздел, где подробно рассказывалось о различных признаках того, что ваш код нуждается в ООП.
Поскольку только sales_periods фактически использует атрибуты экземпляра и возвращает dict, а не другой экземпляр SalesTable, все остальные методы можно вынести из класса и определить как обычные функции:
class SalesTable:
def __init__(self, banner, start_year, start_month, end_year, end_month):
...
def sales_periods(self):
# ...
return some_dict
def find_sales_period_csv(dct):
return some_list
def csv_to_df(lst):
return some_list
def combine_dfs(lst):
return some_df
def check_data(df):
pass
И вы будете называть их всех по цепочке:
x = SalesTable(...)
check_data(combine_dfs(csv_to_df(find_sales_period_csv(x.sales_periods()))))
Теперь внимательно посмотрите на свой класс: у вас есть только два метода, __init__ и sales_periods. Если __init__ не делает что-то дорогое, что вы не хотите повторять (и вы бы вызывали sales_periods для одного и того же экземпляра несколько раз), весь класс можно свести к одной функции, которая объединяет __init__ и метод sales_period:
def sales_periods(banner, start_year, start_month, end_year, end_month):
...
return some_dict
check_data(combine_dfs(csv_to_df(find_sales_period_csv(sales_periods(...)))))
Это имеет большой смысл. Было бы неуместно использовать статические методы в качестве альтернативы, поскольку они не используют никаких атрибутов?
Вы могли бы, но эти функции не звучат так, как будто они имеют какое-либо отношение к SalesTable иначе. Статические методы обычно имеют менее общее назначение, поскольку они четко определены только как помощники для «обычных» методов или, по крайней мере, в некоторой степени связаны с классом, если не с каким-либо конкретным экземпляром класса.
Попался, так что, если, скажем, у меня была другая функция, которая что-то делала с периодами продаж или для них (например, какое-то форматирование и т. д.), и она не использовала атрибуты экземпляра, было бы более уместно использовать ее как статический метод.
Удалите любой метод, который не использует
selfиз класса. По сути, вы спрашиваете о композицииf(g(h(x))), которая не зависит от того, какиеf,gилиhявляются методами или обычными функциями. Использует ли что-нибудь кромеsales_periodsатрибутыSalesTable?