Найти тему
Калинкин

Простые шаги для создания собственного класса на Python

Оглавление
Классы на Python
Классы на Python

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

Прежде чем мы начнем, если вам все еще нужны веские причины для изучения Python, моя подборка может ЗДЕСЬ вам помочь.

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

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

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

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

Создание пользовательского класса на Python с использованием конструктора

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

Давайте напишем простой пустой класс:

class Pokemon:
Pass
# создайте экземпляр класса Pokemon и присвойте его переменной pokemon
pokemon = Pokemon()
print(pokemon)

Вывод:

<__main__.Pokemon object at 0x0000027B56ADD730>

Поскольку наш пользовательский класс Python пуст, он просто возвращает адрес, где хранится объект.

В объектно-ориентированном программировании свойства пользовательского объекта определяются атрибутами, в то время как его методы определяют его поведение. Существует три типа методов:

  • Методы экземпляра
  • Методы класса
  • Статические методы

В Python self ключевое слово представляет экземпляр класса. Он работает как дескриптор для доступа к членам класса, таким как атрибуты из методов класса. Это первый аргумент __init__() метода и вызывается автоматически для инициализации атрибутов класса значениями, определенными пользователем.

Давайте запустим пример:

class Pokemon:
def __init__(self):
print("calling __init__() constructor...")
pokemon = Pokemon()

Вывод:

calling __init__() constructor...

Однако пользовательский класс на Python бесполезен, если он не связан с функциональными возможностями. Функциональные возможности добавляются с помощью атрибутов и действуют как контейнеры для данных и функций для этих атрибутов. Эти функции называются методами.

Атрибуты экземпляра и класса в пользовательском классе Python

Давайте обновим Pokemon класс с помощью init() метода, который создает name атрибуты age. Эти атрибуты называются атрибутами экземпляра.

class Pokemon:
def __init__(self, name, attack):
self.name = name
self.attack = attack

Теперь давайте определим атрибут class для нашего Pokemon класса:

class Pokemon:
# Атрибут класса
species = "Mouse"
def __init__(self, name, attack):
self.name = name
self.attack = attack

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

Давайте создадим несколько покемонов.

class Pokemon:
# Атрибут класса
species = "Mouse"
def __init__(self, name, attack):
self.name = name
self.attack = attack
pikachu = Pokemon("Pikachu", "Double Kick")
raichu = Pokemon("Raichu", "Thunder Punch")

После создания Pokemon экземпляров мы можем получить доступ к их атрибутам экземпляра, используя точечную нотацию, [instance name].[attribute name], например:

>>> pikachu.name
'Pikachu'

>>> pikachu.attack
'Double Kick'

>>> pikachu.species
'Mouse'

>>> raichu.name
'Raichu'

>>> raichu.attack
'Thunder Punch'

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

>>> pikachu.attack = "Thunder Shock"
>>> pikachu.attack
'Thunder Shock'

Методы экземпляра в пользовательских классах Python

Методы экземпляра - это функции, определенные внутри класса, и могут быть вызваны только из экземпляра этого класса. Например, __init__() первым параметром метода экземпляра всегда является self.

Давайте определим некоторые методы экземпляра для нашего пользовательского класса Python Pokemon.

class Pokemon:
# Атрибут класса
species = "Mouse"
def __init__(self, name, attack):
self.name = name
self.attack = attack
# Метод одного экземпляра
def description(self):
return f"{self.name} favorite attack is {self.attack}"
# Метод второго экземпляра
def speak(self, sound):
return f"{self.name} says {sound}"

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

Давайте воспользуемся нашими новыми методами экземпляра, создав новый Pokemon экземпляр динамически:

>>> pichu = Pokemon("Pichu", "Nuzzle")
>>> pichu.description()
"Pichu favorite attack's is Nuzzle"

>>> pichu.speak("pichu pichu")
'Pichu says pichu pichu'

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

Методы класса в пользовательских классах Python

Метод class существует для установки или получения статуса класса. Они не могут получить доступ к данным конкретного экземпляра или изменить их. Методы предназначены для описания поведения объектов и определены внутри класса.

Метод класса должен быть определен с помощью @classmethod декоратора. Они также принимают один параметр по умолчанию cls, который указывает на класс. Называть его не обязательно cls, но полезно следовать соглашениям.

Методы класса используются для создания фабричного метода. Фабричные методы возвращают разные объекты класса в зависимости от варианта использования.

Давайте продолжим с Pokemon:

class Pokemon:
def __init__(self, names):
self.names = names
def __repr__(self):
return f'Pokemon({self.names})'
@classmethod
def mouse(cls):
return cls(['Pichu', 'Pikachu', 'Raichu'])
@classmethod
def hummingbird(cls):
return cls(['Florabri', 'Floressum'])

Вместо прямого вызова Pokemon конструктора я использую cls аргумент в методах класса mouse и hummingbird. Когда я меняю имя класса, мне не нужно обновлять имя конструктора в каждом методе класса.

__repr__() используется для представления объекта класса в виде строки. Это означает, что результат представляет собой строковое представление объекта. Без этого вывод Pokemon.mouse() будет:

>>> Pokemon.mouse()
<__main__.Pokemon at 0x1d219dcb4f0>

Вот что делают эти методы класса:

>>> Pokemon.mouse()
Pokemon(['Pichu', 'Pikachu', 'Raichu'])

>>> Pokemon.hummingbird()
Pokemon(['Florabri', 'Floressum'])

Итак, мы использовали методы класса для создания новых объектов Pokemon, уже настроенных так, как мы хотим.

Статические методы в пользовательских классах Python

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

Чтобы определить их, нам нужно использовать @staticmethod декоратор. В отличие от методов экземпляра и класса, нам не нужно передавать какой-либо параметр по умолчанию.

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

class Pokemon:
def __init__(self, power, level, names):
self.power = power
self.level = level
self.names = names
def __repr__(self):
return (f'Pokemon({self.power}, '
f'{self.level}, '
f'{self.names})')
def total_damage(self):
return self.damage(self.power, self.level)
@staticmethod
def damage(power, level):
return (power * level * 2) / 50

Я изменил конструктор, чтобы он принимал аргументы power и level и __repr__() отображал его. Я также добавил total_damage() метод экземпляра, который вычисляет и возвращает ущерб при атаках покемонов. И, вместо того, чтобы вычислять уровень ущерба непосредственно внутри total_damage(), я добавил формулу для вычисления ущерба в отдельном damage() статическом методе.

Давайте попробуем:

>>> charmander = Pokemon(20, 8, "Charmander")
>>> charmander.total_damage()
6.4

>>> charmander.damage(20, 8)
6.4

Вариант использования здесь очень прост. Статические методы не имеют доступа к cls или self. Они ведут себя как обычные функции, но принадлежат пространству имен класса и обычно не привязаны к жизненному циклу объекта. Приведенный выше damage() метод полностью независим от класса, что делает тестирование намного более управляемым.

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

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

class NewClass:
def method(self):
return 'Calling instance method...', self
@classmethod
def classmethod(cls):
return 'Calling class method...', cls
@staticmethod
def staticmethod():
return 'Calling static method...'

>>> NewClass.method()
TypeError: method() missing 1 required positional argument: 'self'

>>> NewClass.classmethod()
('Calling class method...', __main__.NewClass)

>>> NewClass.staticmethod()
'Calling static method...'

Мы смогли вызвать classmethod() и staticmethod(), но попытка вызвать метод экземпляра method() завершилась неудачей из-за ошибки типа. Это потому, что мы пытались вызвать функцию экземпляра непосредственно в самой схеме элементов класса, не создавая экземпляр класса. Python не смог заполнить аргумент self, что привело к сбою вызова. То, что мы можем вызвать staticmethod() без проблем, напротив, доказывает, что метод полностью независим от остальной части класса.

Видимость в пользовательских классах Python

Объектно-ориентированные языки программирования, такие как C ++ и Java, контролируют доступ к классам с открытыми, закрытыми и защищенными ключевыми словами. Python концептуализирует модификаторы публичного, защищенного и частного доступа, в отличие от других языков, таких как C #, Java и C ++.

Общедоступные члены пользовательского класса в Python

К общедоступным членам можно получить доступ извне класса. Это означает, что мы можем свободно изменять атрибуты класса без каких-либо ограничений. Для вызова открытого метода требуется тот же объект класса. Это делается для того, чтобы следовать принципу инкапсуляции данных. В Python члены класса по умолчанию являются общедоступными.

Защищенные члены пользовательского класса в Python

Защищенные члены класса доступны изнутри класса, а также доступны для его подклассов.

В Python нет никакого механизма для ограничения доступа к какой-либо переменной экземпляра или методу. Вместо этого в нем используется соглашение о предварении имени переменной или метода одинарным или двойным подчеркиванием для эмуляции поведения спецификаторов защищенного и частного доступа. Для защиты переменной экземпляра добавляется префикс с одним подчеркиванием (“_”), что предотвращает доступ к нему только внутри подкласса.

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

Частные члены пользовательского класса в Python

Закрытые члены класса не могут получить доступ к среде извне класса и могут обрабатываться только изнутри самого класса. Любая попытка изменить переменную приводит к AttributeError.

Имя закрытого элемента присваивается путем добавления префикса двойного подчеркивания (“__”) перед именем переменной. Python выполняет искажение имен частных переменных, и каждый элемент с двойным подчеркиванием изменяется на _object._class__variable. Таким образом, к нему все еще можно получить доступ извне класса, но такой практики следует избегать.

Методы сравнения двух пользовательских объектов Python

При написании кода мы используем такие операторы, как > . Тем не менее, вам нужно использовать __gt__() и подобные для реализации функциональности вашего пользовательского класса Python.

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

class Value:
def __init__(self, baz):
self.baz = baz
# Меньше, чем оператор
def __lt__(self, obj2):
return self.baz < obj2.baz
# Больше, чем оператор
def __gt__(self, obj2):
return self.baz > obj2.baz
# Оператор меньше или равен
def __le__(self, obj2):
return self.baz <= obj2.baz
# Оператор больше или равен
def __ge__(self, obj2):
return self.baz >= obj2.baz
# Оператор равенства
def __eq__(self, obj2):
return self.baz == obj2.baz
# неравный (не равный) оператор
def __ne__(self, obj2):
return self.baz != obj2.baz
foo = Value(6)
bar = Value(9)
print(
foo < bar,
foo > bar,
foo <= bar,
foo >= bar,
foo == bar,
foo != bar
)

Вывод:

True False True False False True

Экземпляры foo и bar содержат атрибут с именем foo, который содержит целочисленные значения 6 и 9 соответственно. Это очень простой пример. Ваши методы могут использовать более сложные операции; например, они могут сравнивать несколько разных атрибутов одновременно.

Математические операции над двумя пользовательскими объектами Python

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

class Value:
def __init__(self, baz):
self.baz = baz
# Добавление двух объектов
def __add__(self, obj2):
return self.baz + obj2.baz
# Subtracting two objects
def __sub__(self, obj2):
return self.baz - obj2.baz
# Умножение двух объектов
def __mul__(self, obj2):
return self.baz * obj2.baz
# Разделение двух объектов
def __truediv__(self, obj2):
return self.baz / obj2.baz
# Получаем остаток от деления двух объектов
def __mod__(self, obj2):
return self.baz % obj2.baz
foo = Value(2)
bar = Value(4)
print(
foo + bar,
foo - bar,
foo * bar,
foo / bar,
foo % bar,
)

Который выдает результат:

6 -2 8 0.5 2

Для получения дополнительной информации о классах Python ознакомьтесь с документацией ЗДЕСЬ.

Заключительные мысли о пользовательском классе в Python

В этой вводной статье я подробно рассказали о создании пользовательских классов и объектов Python. Я решил не затрагивать тему деструкторов, которые вызываются при уничтожении объекта. В отличие от других языков программирования, таких как C ++, Python имеет сборщик мусора с автоматическим управлением памятью, что делает деструкторы менее необходимыми.

Наука
7 млн интересуются