Part 8

Определение классов

Класс определяется с помощью ключевого слова class. Синтаксис выглядит так:

class NameOfClass:
    # class defition goes here

Имена классов обычно пишут в стиле PascalCase, также известном как UpperCamelCase. Это означает, что все слова в имени класса пишутся вместе, без пробелов, и каждое слово начинается с заглавной буквы. Следующие имена классов соответствуют этому соглашению:

  • Weekday
  • BankAccount
  • LibraryDatabase
  • PythonCourseGrades

Одно определение класса должно представлять одно целое, содержимое которого каким-то образом атомарно связано между собой. В более сложных программах классы могут содержать элементы других классов. Например, класс Course мог бы содержать объекты класса Lecture, ExerciseSession и т. д.

Давайте посмотрим на каркас определения класса. На этом этапе функциональность еще отсутствует.

class BankAccount:
    pass

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

Рассмотрим программу, где к объекту BankAccount добавляются две переменные: balance и owner. Любые переменные, привязанные к объекту, называются его атрибутами, или, точнее, атрибутами данных, а иногда переменными экземпляра.

К атрибутам, привязанным к объекту, можно обращаться через объект:

class BankAccount:
    pass

peters_account = BankAccount()
peters_account.owner = "Peter Python"
peters_account.balance = 5.0

print(peters_account.owner)
print(peters_account.balance)
Пример вывода

Peter Python 5.0

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

account = BankAccount()
account.balance = 155.50

print(account.balance) # This refers to the data attribute balance attached to the account
print(balance) # THIS CAUSES AN ERROR, as there is no such independent variable available, and the object reference is missing

Добавление конструктора

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

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

class BankAccount:
    pass

peters_account = BankAccount()
peters_account.owner = "Peter"
peters_account.balance = 1400

paulas_account = BankAccount()
paulas_account.owner = "Paula"

print(peters_account.balance)
print(paulas_account.balance) # THIS CAUSES AN ERROR

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

Метод-конструктор — это объявление метода со специальным именем __init__, обычно расположенное в самом начале определения класса.

Посмотрим на класс BankAccount с добавленным методом-конструктором:

class BankAccount:

    # The constructor
    def __init__(self, balance: float, owner: str):
        self.balance = balance
        self.owner = owner

Имя метода-конструктора всегда __init__. Обратите внимание на два подчеркивания с обеих сторон слова init.

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

self.balance = balance

присваивает баланс, полученный как аргумент, атрибуту balance объекта. Распространенное соглашение — использовать одинаковые имена переменных для параметров и атрибутов данных, определяемых в конструкторе, но имена переменных self.balance и balance выше относятся к двум разным переменным:

  • Переменная self.balance — атрибут объекта. У каждого объекта BankAccount есть собственный баланс.

  • Переменная balance — параметр метода-конструктора __init__. Ее значение устанавливается равным значению, переданному как аргумент методу при вызове конструктора (то есть при создании нового экземпляра класса).

Теперь, когда мы определили параметры метода-конструктора, мы можем передать желаемые начальные значения атрибутов данных как аргументы при создании нового объекта:

class BankAccount:

    # The constructor
    def __init__(self, balance: float, owner: str):
        self.balance = balance
        self.owner = owner

# As the method is called, no argument should be given for the self parameter
# Python assigns the value for self automatically
peters_account = BankAccount(100, "Peter Python")
paulas_account = BankAccount(20000, "Paula Pythons")

print(peters_account.balance)
print(paulas_account.balance)
Пример вывода

100 20000

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

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

class BankAccount:

    # The constructor
    def __init__(self, balance: float, owner: str):
        self.balance = balance
        self.owner = owner

peters_account = BankAccount(100, "Peter Python")
print(peters_account.balance)

# Change the balance to 1500
peters_account.balance = 1500
print(peters_account.balance)

# Add 2000 to the balance
peters_account.balance += 2000
print(peters_account.balance)
Пример вывода

100 1500 3500

Посмотрим еще на один пример классов и объектов. Мы напишем класс, который моделирует один розыгрыш лотерейных чисел:

from datetime import date

class LotteryDraw:

    def __init__(self, round_week: int, round_date: date, numbers: list):
        self.round_week = round_week
        self.round_date = round_date
        self.numbers = numbers


# Create a new LotteryDraw object
round1 = LotteryDraw(1, date(2021, 1, 2), [1,4,8,12,13,14,33])

# Tulostetaan tiedot
print(round1.round_week)
print(round1.round_date)

for number in round1.numbers:
    print(number)
Пример вывода

1 2021-01-02 1 4 8 12 13 14 33

Как видно выше, атрибуты могут быть любого типа. Здесь у каждого объекта LotteryDraw есть атрибуты типов list и date.

Loading
Loading

Использование объектов, созданных из ваших собственных классов

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

# this function creates a new bank account object and returns it
def open_account(name: str):
    new_account =  BankAccount(0, name)
    return new_account

# this function adds the amount passed as an argument to the balance of the bank account also passed as an argument
def deposit_money_on_account(account: BankAccount, amount: int):
    account.balance += amount

peters_account = open_account("Peter Python")
print(peters_account.balance)

deposit_money_on_account(peters_account, 500)

print(peters_account.balance)
Пример вывода

0 500

Loading
Loading
Loading
Вы дошли до конца этого раздела! Перейти к следующему разделу:

Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.