DevGang
Авторизоваться

Как эффективно использовать классы Python 

"Здесь должен быть только один - и желательно только один - очевидный способ сделать это» - говорит дзен Python. Тем не менее, есть области, где даже опытные программисты спорят о том, что делать, а что нет.

Одна из этих областей - классы Python. Заимствованные из объектно-ориентированного программирования, это довольно красивые конструкции, которые вы можете расширять и изменять по мере написания кода.

Большая проблема в том, что классы могут сделать ваш код более сложным, чем необходимо, и затруднить его чтение и поддержку. Итак, когда вам следует использовать классы, а когда вместо них использовать стандартные функции?

Эта история - более глубокое погружение в суть дела. Так что, если вы спешите, вы можете пропустить следующие два раздела и прокрутить вниз до разделов "Когда использовать классы" и "Когда классы - плохая идея".

Классы Python: самые основы

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

Вы можете создать класс, который берет каждый предмет одежды в магазине и хранит ключевые параметры, такие как тип одежды, ее цвет и размер. Мы также добавим возможность добавления цены.

class Clothing(object):
    def __init__(self, type, color, size, price=None):
        self.type = type
        self.color = color
        self.size = size
        self.price = price

Теперь мы можем определять различные экземпляры класса и поддерживать их порядок:

bluejeans = Clothing("jeans", "blue", 12)
redtshirt = Clothing("t-shirt", "red", 10, 10)

Мы бы добавили эти две строки без отступа после определения класса. Этот код будет работать, но мало что делает. Мы можем добавить метод для установки цены непосредственно под функцией __init__ в определении класса:

    def set_price(self, price):
        """Set the price of an item of clothing."""
        self.price = price
        print(f"Setting the price of the {self.color} {self.type} to ${price}.")

Мы также могли бы добавить несколько процедур, чтобы сообщать нам цену или продвигать товар за счет снижения цены:

    def get_price(self):
        """Get the price of an item of clothing, if price is set."""
        try:
            print(f"The {self.color} {self.type} costs ${self.price}.")
        except:
            print(f"The price of the {self.color} {self.type} hasn't been set yet!")
    def promote(self, percentage):
        """Lower the price, if initial price is set."""
        try:
            self.price = self.price * (1-percentage/100)
            print(f"The price of the {self.color} {self.type} has been reduced by {percentage} percent! It now only costs ${self.price:.0f}.")
        except:
            print(f"Oops. Set an initial price first!")

Теперь мы можем добавить несколько вызовов наших методов после строк, в которых мы инициализировали экземпляры класса:

print("blue jeans -------------------")
bluejeans.promote(20)
bluejeans.set_price(30)
bluejeans.get_price()

print("red t-shirt ------------------")
redtshirt.get_price()
redtshirt.promote(20)

Если вы запустите сценарий, результат будет следующим:

blue jeans -------------------
Oops. Set an initial price first!
Setting the price of the blue jeans to $30.
The blue jeans costs $30.
red t-shirt ------------------
The red t-shirt costs $10.
The price of the red t-shirt has been reduced by 20 percent! It now only costs $8.

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

Самое приятное во всем этом то, что вы можете добавлять и удалять столько объектов, сколько захотите. Удаление атрибута происходит так:

del redtshirt.price 

А если вы хотите удалить весь объект, вы делаете так:

del redtshirt

Все это аккуратно, просто и расширяемо. Попробуйте реализовать эту реализацию со стандартными функциями, и у вас, вероятно, будет гораздо больше проблем с этим.

С теоретической точки зрения, есть и другие причины, по которым классы Python являются прекрасной концепцией во многих ситуациях.

Классы потрясающие - теоретически

Разделение забот: дать каждому классу свою работу

Если вы посещали лекции по информатике, вполне вероятно, что вы наткнулись на принцип «разделения интересов». По сути, это означает, что вы разбиваете свою программу на разные разделы, которые имеют дело с разной информацией.

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

Развязка: упрощение обслуживания

Классическое мышление помогает не только разделять функции, но и независимы друг от друга. Это не только сохраняет вещи в чистоте и порядке; его также намного проще обслуживать.

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

Скрытие реализации: определение того, что программисты могут и не могут использовать

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

Инкапсуляция: изменение кода, но не взаимодействия с пользователем

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

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

Вполне возможно создавать функции черного ящика без использования классов.

Наследование: написание ДНК структуры данных

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

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

Когда использовать классы

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

Объединение данных и методов вместе

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

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

Остерегайтесь глобальных переменных

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

Когда классы - плохая идея

Использование heapq или кучи

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

Если куча лучше подходит для ваших целей, вам не нужно определять класс. Встроенный в Python алгоритм heapq очереди кучи сделает всю работу за вас.

Рассмотрите возможность использования functools.partial()

У вас может возникнуть соблазн использовать класс, потому что вы постоянно вызываете функцию с одними и теми же аргументами. В большинстве случаев лучше использовать functools.partial() вместо него.

Реализовать довольно просто. Допустим, у вас есть функция, которая умножает два значения, но вы продолжаете использовать ее для удвоения значений. Чтобы избежать дублирования кода, вы можете написать это:

from functools import partial

def multiply(x,y):
    return x * y

doubling = partial(multiply,2)
print(doubling(4))

Намного проще, чем определять новый класс!

Определение на «будущее»

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

class newclass:
    """defining a new class to do something awesome"""
    pass

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

Угадайте, что делают эти три строки кода? Ровно ничего. И эти строки совсем не сложно закодировать. Если вы думаете, что позже вам понадобится еще один класс, и действительно думаете, что можете забыть об этом в будущем, вы всегда можете оставить такой комментарий:

# initiate a new class here if needed for purpose XY

Хотя вы захотите сделать свой код расширяемым и защищенным от идиотов, инициализация класса, который ничего не делает, обычно является плохой идеей.

Итог: классы Python - это палка о двух концах

Классы, без сомнения, мощная концепция. При правильном использовании они могут сделать ваш код более аккуратным, читаемым и удобным в обслуживании.

Но ими часто злоупотребляют. А при неправильном использовании они могут загрязнять ваш код, пока вы ничего не поймете.

Иногда, особенно в более простых программах, вы можете использовать класс или набор общих функций, и код будет очень похож по длине и сложности. По мере того, как программы становятся более сложными, различия становятся более заметными.

В этом смысле дзен Python подтвердил свой вердикт: в большинстве случаев действительно существует только один хороший способ делать что-то, будь то с классами или без них. Однако это не всегда полностью очевидно. Трудно понять, какой путь лучше.

Источник:

#Python
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу