Classes поєднують дані з поведінкою.
Класи використовують, щоб створювати копії, або instances, обʼєднаних даних і поведінки, які зазвичай називають objects.
Objects можуть представляти реальні сутності (наприклад, машини чи коти) - або абстрактніші поняття (наприклад, цілі числа, транспортні засоби чи ссавці).
Класи є невіддільною частиною підходу обʼєктно-орієнтованого програмування (ООП), який пропонує програмістові думати про задачу як про один або кілька обʼєктів, що взаємодіють між собою, зберігають стан і діють на основі даних.
Класи - це визначення нових типів обʼєктів, з яких створюються нові instances обʼєктів.
Вони часто обʼєднують дані (відомі як fields, attributes, properties, data members або variables) з кодом або функціями (відомими як methods), які працюють із цими даними.
У цьому сенсі клас - це креслення або набір вказівок, за якими можна побудувати й використовувати багато обʼєктів схожого типу.
Складна програма може мати багато класів, і кожен з них будує багато різновидів обʼєктів.
Процес побудови й налаштування обʼєкта з класу називають instantiation (або створенням екземпляра класу).
Визначення класу в Python досить просте:
class MyClass:
# Class body goes here
Поля класу (їх також називають властивостями, атрибутами, елементами даних або змінними) можна додавати до тіла класу.
class MyClass:
number = 5
string = "Hello!"
Екземпляр (обʼєкт) MyClass можна створити й привʼязати до імені:
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
Class attributes спільні для всіх обʼєктів (або екземплярів), створених з класу, і до них можна звертатися через dot notation - крапку ., яку ставлять після імені обʼєкта й перед іменем атрибута:
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
Атрибути класу визначають у тілі самого класу, перед будь-якими іншими методами. Вони належать класу, що дає змогу ділити їх між екземплярами. Оскільки ці атрибути спільні для всіх екземплярів класу, до їхнього значення можна звертатися й змінювати його безпосередньо з класу. Зміна значення атрибутів класу змінює значення для всіх обʼєктів, створених з цього класу:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
Мати цілу купу обʼєктів із постійно синхронізованими даними не надто корисно.
На щастя, обʼєкти, створені з класу, можна налаштувати за допомогою власних instance attributes (або властивостей, змінних чи полів екземпляра).
Як випливає з назви, атрибути екземпляра унікальні для кожного обʼєкта, і їх можна змінювати незалежно.
__init__()
Спеціальний «dunder-метод» (скор. від «метод із подвійним підкресленням») __init__() використовують, щоб налаштовувати екземпляри класу, і він може ініціалізувати атрибути чи властивості екземпляра для обʼєктів.
Через свою роль в ініціалізації атрибутів екземпляра __init__() також називають class constructor або initializer.
__init__() приймає один обовʼязковий параметр self, який указує на щойно ініційований або створений обʼєкт.
Дані для атрибутів чи властивостей екземпляра можна потім передати як аргументи __init__() після параметра self.
Нижче MyClass тепер має атрибути екземпляра location_x і location_y.
Як бачимо, ці два атрибути присвоєно першому й другому індексам аргументу location (кортеж), переданого в __init__().
Атрибути location_x і location_y для екземпляра класу тепер ініціалізуються, коли ми створюємо екземпляр класу й отримуємо обʼєкт:
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
Зауважмо, що під час ініціалізації MyClass вище потрібно передати лише один аргумент: Python сам подбає про передавання self, коли ми викликаємо клас.
Ще один спосіб створити змінну екземпляра - просто звернутися до змінної класу через обʼєкт і змінити її на щось інше:
>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'
>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'
>>> obj_one.string
'Hello!'
А тепер подивімося, що станеться, коли змінна класу зміниться:
>>> MyClass.string = "World!"
>>> obj_two.string
# obj_two.string has not changed
'Hi!'
>>> obj_one.string
# obj_one.string changed
'World!'
Атрибут string тепер є змінною екземпляра у випадку obj_two, але залишається змінною класу в obj_one.
Це також можна зробити зсередини класу:
class Demo:
new_var = 3
def add_two(self):
self.new_var += 2
У момент, коли викликається <object>.add_two() і виконується self.new_var += 2, new_var перетворюється зі змінної класу на змінну екземпляра з тією ж назвою.
Це може стати в пригоді під час ініціалізації, коли всім екземплярам класу потрібно, щоб певні атрибути мали однакове початкове значення.
Однак тоді змінна екземпляра затіняє змінну класу, роблячи змінну класу недоступною з того екземпляра, де її затінено.
З огляду на це, може бути безпечніше й зрозуміліше задавати атрибути екземпляра в методі __init__() як self.<attribute>.
method - це function, привʼязана або до самого класу (її називають методом класу, докладніше про це йтиметься в пізнішій вправі), або до екземпляра класу (обʼєкта).
Методи, які працюють з обʼєктом чи екземпляром, потрібно визначати з self як першим параметром.
Решту параметрів можна визначити так само, як для «звичайної» або непривʼязаної функції.
Методи, які працюють з класом, потрібно визначати з декоратором @classmethod і (за домовленістю) з cls як першим параметром.
Методи класу викликають безпосередньо на класі, не створюючи спершу обʼєкт із цього класу.
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Class method. Uses the @classmethod decorator, and cls as the first parameter.
# Can be called without first making an instance of the class.
@classmethod
def change_string(cls, new_string):
#Class attributes are referred to with cls.
cls.string = new_string
return cls.string
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
Як і у випадку з доступом до атрибутів, викликати метод так само просто: достатньо поставити . після імені обʼєкта й перед іменем методу.
Викликаному методу не потрібна копія обʼєкта як перший параметр, адже Python підставляє self автоматично:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
До атрибутів класу можна звертатися зсередини методів екземпляра так само, як і поза класом:
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
pass
У попередніх концептуальних вправах і в заготовках практичних вправ ми бачили, як у тілі функцій замість справжнього коду використовують ключове слово pass.
Ключове слово pass - це синтаксично припустима заглушка: воно заважає Python видавати синтаксичну помилку для порожнього визначення функції чи класу.
По суті, це спосіб сказати інтерпретатору Python: «Не хвилюйся! Я обовʼязково напишу тут код, просто ще не зробив цього».
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or place-holding the body of this method.
pass