Когда я должен использовать классы в Python?


я программирую на python около двух лет; в основном данные (панды, mpl, numpy), но также сценарии автоматизации и небольшие веб-приложения. Я пытаюсь стать лучшим программистом и увеличить свои знания python, и одна из вещей, которая меня беспокоит, заключается в том, что я никогда не использовал класс (за исключением копирования случайного кода колбы для небольших веб-приложений). Я вообще понимаю, что это такое, но я не могу понять, почему мне нужны они для простой функции.

чтобы добавить специфику к моему вопросу: я пишу тонны автоматических отчетов, которые всегда включают в себя извлечение данных из нескольких источников данных (mongo, sql, postgres, API), выполняя много или мало данных munging и форматирование, запись данных в csv/excel/html, отправить его по электронной почте. Сценарии варьируются от ~250 строк до ~600 строк. Будет ли у меня какая-то причина использовать классы для этого и почему?

4 52

4 ответа:

классы являются опорой Объектно-Ориентированное Программирование. ООП очень озабочена организацией кода, возможностью повторного использования и инкапсуляцией.

во-первых, отказ от ответственности: ООП частично в отличие от Функциональное Программирование, который является другой парадигмой, используемой много в Python. Не все, кто программирует на Python (или, Конечно, большинство языков) использует ООП. Вы можете сделать много в Java 8, который не очень ориентирован на объект. Если вы не хотите использовать ООП, то не надо. Если вы просто пишете одноразовые сценарии для обработки данных, которые вы никогда не будете использовать снова, то продолжайте писать так, как вы есть.

однако, есть много причин, чтобы использовать ООП.

некоторым причинам:

  • организация: ООП определяет хорошо известные и стандартные способы описания и определения данных и процедур в коде. Как данные, так и процедуры могут храниться на разных уровнях определения (в разных классах), и существуют стандартные способы говоря об этих определениях. То есть, если вы используете ООП стандартным способом, это поможет потом себе и другим понять, изменить, и использовать ваш код. Кроме того, вместо использования сложного, произвольного механизма хранения данных (диктовки диктовок или списков или диктовки или списки диктовок наборов, или что-то еще), вы можете назвать части структур данных и удобно ссылаться на них.

  • состояние: ООП помогает определить и отслеживать состояние. Например, в классическом примере, если вы создавая программу, которая обрабатывает студентов (например, программа оценки), вы можете хранить всю необходимую информацию о них в одном месте (имя, возраст, пол, уровень оценки, курсы, оценки, учителя, сверстники, диета, особые потребности и т. д.), и эти данные сохраняются до тех пор, пока объект жив, и легко доступны.

  • инкапсуляция: При инкапсуляции процедура и данные хранятся вместе. Методы (термин ООП для функций) определяются правильно наряду с данными, над которыми они работают и производят. На таком языке, как Java, который позволяет контроль доступа, или в Python, в зависимости от того, как вы описываете свой публичный API, это означает, что методы и данные могут быть скрыты от пользователя. Это означает, что если вам нужно или вы хотите изменить код, вы можете делать все, что хотите для реализации кода, но сохранить общедоступные API-интерфейсы одинаковыми.

  • наследование: Наследование позволяет вам чтобы определить данные и процедуру в одном месте (в одном классе), а затем переопределить или расширить эту функциональность позже. Например, в Python я часто вижу, как люди создают подклассы dict класс для добавления дополнительной функциональности. Общим изменением является переопределение метода, который создает исключение, когда ключ запрашивается из несуществующего словаря, чтобы дать значение по умолчанию на основе неизвестного ключа. Это позволяет вам расширить свой собственный код сейчас или позже, позволить другим расширить ваш код, и позволяет расширить код других людей.

  • повторное использование: все эти причины и другие, позволяют увеличить повторное использование кода. Объектно-ориентированный код позволяет писать твердый (проверенный) код один раз, а затем повторно использовать снова и снова. Если вам нужно что-то подкрутить для вашего конкретного случая использования, вы можете наследовать от существующего класса и перезаписать существующее поведение. Если вам нужно что-то изменить, вы можете изменить все это при сохранении существующего общественного метод подписи, и никто не мудрее (надеюсь).

опять же, есть несколько причин не использовать ООП, и вам не нужно. Но, к счастью, с таким языком, как Python, вы можете использовать только немного или много, это зависит от вас.

пример использования студента (нет гарантии качества кода, только пример):

Объектно-Ориентированное

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

Стандартные Дикт

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))

всякий раз, когда вам нужно поддерживать состояние ваших функций, и это не может быть достигнуто с помощью генераторов (функции, которые дают, а не возвращают). Генераторы поддерживают свое собственное состояние.

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

всякий раз, когда у вас есть использование для шаблона посетителя, вам понадобятся классы. Любой другой шаблон проектирования может быть выполнен более эффективно и чисто с генераторами, контекстными менеджерами (которые также лучше реализованы как генераторы, чем как классы) и типы POD (словари, списки и кортежи и т. д.).

Если вы хотите написать "pythonic" код, вы должны предпочесть контекстные менеджеры и генераторы над классами. Это будет чище.

Если вы хотите расширить функциональность, вы почти всегда сможете сделать это с ограничения, а не наследование.

Как и каждое правило, это имеет исключение. Если вы хотите инкапсулировать функциональность быстро (т. е. написать тестовый код, а не многоразовый код на уровне библиотеки), вы можете инкапсулировать состояние в классе. Это будет просто и не нужно будет повторно использовать.

Если вам нужен деструктор стиля C++ (RIIA), вы определенно не хотите использовать классы. Вам нужны контекстные менеджеры.

Я думаю, что вы делаете это правильно. Занятия разумны, когда нужно смоделировать какую-то бизнес-логику или сложные реальные процессы со сложными отношениями. Как пример:

  • несколько функций с общим состоянием
  • более одной копии одних и тех же переменных состояния
  • чтобы расширить поведение существующей функциональности

Я также предлагаю вам посмотреть Это классическое видео

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

Если это не так, нет необходимости создавать класс