Об'єктно-орієнтоване програмування на Python: класи, опис і особливості
Опубликованно 04.01.2018 02:02
В Python класи є фундаментальним поняттям. Це основа стандартної бібліотеки, роботи більшості популярних програм і самої мови. Якщо ви хочете стати більше, ніж просто початківцям програмістом, ви повинні розуміти суть і принцип роботи з класами та об'єктами.
Що таке класи
Це базовий програмний компонент ООП. В Python класи використовуються для реалізації нових типів об'єктів і створюються за допомогою спеціальної інструкції class. Зовні вони нагадують стандартні вбудовані види даних, такі як числа або послідовності. Але у об'єктів класу є суттєва відмінність – підтримка спадкування.
Об'єктно-орієнтоване програмування на Python повністю базується на ієрархічному спадкування класів. Це універсальний спосіб адаптації та багаторазового використання коду. Але об'єктно-орієнтований підхід не є обов'язковим. Python без проблем допускає виключно процедурне та функціональне програмування.
Головне завдання класів у Python – упаковка даних і виконуваного коду. Синтаксично вони схожі на інструкції def. Подібно функцій, вони створюють свої простори імен, які можна неодноразово викликати з будь-якої частини програми. Навіщо ж тоді вони потрібні? Класи – це більш потужний і універсальний інструмент. Найбільше їх потенціал розкривається у момент створення нових об'єктів.
Важливість класів і принцип успадкування
У кожного об'єкта є свій простір імен, яке можна програмувати, вводити змінні і створювати будь-які функції. А також є атрибути, успадковані від класу object.attribute. У цьому полягає сенс ООП.
Завдяки спадкуванню, створюється дерево ієрархії. На практиці це виглядає наступним чином. Коли інтерпретатор зустрічає вираз object.attribute, він починає шукати перше входження attribute у зазначеному class. Не виявивши attribute, інтерпретатор продовжує пошук у всіх пов'язаних класах, які перебувають у дереві вище, у напрямку зліва направо.
У древо пошуку входять:суперклассы, які знаходяться на самому верху ієрархії і реалізують загальну поведінку;підкласи – знаходяться нижче;примірники – елементи програми з успадкованим поведінкою.
На малюнку зображено дерево класів Python. З прикладу видно, що Class 2 і 3 – це суперклассы. У самому низу знаходяться два примірники Instance 1 і 2, в середині – підклас Class 1. Якщо написати вираз Instance2.w, воно змусить інтерпретатор шукати значення атрибута .w в такому порядку:Instance2;Class1;Class2;Class3.
Ім'я .w ,буде знайдено в суперклассе Class3. На термінології ООП – це означає, що Instance 2 «успадковує» атрибут .w від Class3.
Зверніть увагу, що примірники на малюнку успадковують тільки чотири атрибути: .w, .x, .y .z:Для примірників Instance1.x і Instance2.x атрибут .x буде знайдений Class 1, де пошук зупиниться, тому що Class 1 знаходиться в дереві нижче, ніж Class 2.Для Instance1.y і Instance2.y атрибут .y буде знайдений Class 1, де пошук зупиниться, тому що це єдине місце, де він з'являється.Для примірників Instance1.z і Instance2.z інтерпретатор знайде .z Class 2, тому що він розташовується в дереві лівіше, ніж Class3.Для Instance2.атрибут name .name буде знайдений в Instance2 без пошуку по дереву.
Передостанній пункт є найбільш важливим. Він демонструє, як Class 1 перевизначає атрибут .x, заміщаючи версію .x суперкласу Class 2. Об'єкти, примірники і методи
ООП оперує двома головними поняттями: класи і об'єкти. Класи створюють нові типи, а об'єкти класів в Python є їх екземплярами. Наприклад, всі цілочисельні змінні відносяться до інтегрованого типу даних int. На мові ООП вони є екземплярами класу int.
Класи створюються інструкціями, а об'єкти з допомогою викликів. Вони можуть зберігати дані і володіти своїм функціоналом або методами класів. В Python термінологія відіграє важливу роль. З її допомогою програмісти відрізняють незалежні функції від тих, що належать класів. Змінні, що відносяться до об'єктів, називають полями.
Розрізняють два види полів в ООП. Перший – це змінні, які належать цілого класу, другий – змінні окремих екземплярів. Поля і методи разом є атрибутами класу. В Python вони записуються в блоці коду після ключового слова class.
Методи та значення self
Методи – це функції з додатковим ім'ям self. Воно додається до початку списку параметрів. При бажанні змінну можна назвати іншим ім'ям, але така ініціатива серед програмістів не вітається. Self – це стандартне, легко впізнаване в коді ім'я. Тим більше на роботу з ним розраховані деякі середовища розробки.
Щоб краще зрозуміти значення self в ООП, уявімо, що у нас є клас з ім'ям ClassA і методом methodA:>>>class ClassA;def methodA (self, аргумент1, аргумент2).
Об'єкт objectA є екземпляром ClassA і виклик методу виглядає наступним чином:>>>objectA.methodA(аргумент1, аргумент2).
Коли інтерпретатор бачить цю сходинку, він автоматично перетворює наступним чином: ClassA.methodA(objectA, аргумент1, аргумент2). Тобто екземпляр класу використовує змінну self як посилання на самого себе.
Як створювати змінні, методи і екземпляри класів
Пропонуємо розібрати практичний приклад з інтерактивної оболонки Python. Створення класу «ЭкспериментПервый» починається з складовою інструкції class:>>>class ЭкспериментПервый:def setinf(self, значення): #створюємо метод перший з аргументамиself.data = значенняdef display(self): #метод другийprint(self.data) #надрукувати дані примірника.
Після обов'язкового відступу слід блок з вкладеними інструкціями def, в яких об'єктах функцій присвоюються імена setinf і display. З їх допомогою створюються атрибути ЭкспериментПервый.setinf і ЭкспериментПервый.display. Фактично будь-яке ім'я, яким присвоюється значення на верхньому рівні у вкладеному блоці, стає атрибутом.
Щоб побачити, як працюють методи, необхідно створити два примірника:>>>x = ЭкспериментПервый () # Створюються два примірники;>>>y = ЭкспериментПервый () # Кожен є окремим простором імен.
Спочатку екземпляри не зберігають ніякої інформації і абсолютно порожні. Але вони пов'язані зі своїм класом:>>>x.setinf(«Вчимо Пітон») #Виклик методу, в якому self – x.>>>y.setinf(3.14) #Еквівалентно: ЭкспериментПервый.setinf(y, 3.14)
Якщо через ім'я примірників x, y звернутися до атрибуту .setinf об'єкта класу ЭкспериментПервый, то в результаті пошуку по дереву спадкування інтерпретатор повертає значення атрибута класу.>>>x.display() #У x і y свої значення self.dataВчимо Пітон>>>y.display()3.14.
Перевантаження операторів
В мові Python класи можуть перевантажувати оператори виразів. Така можливість робить примірники схожими на вбудовані типи даних. Процес полягає в реалізації методів зі спеціальними іменами, що починаються і закінчуються подвійним підкресленням.
Розглянемо в дії __init__ і __sub__. Перший метод називають конструктором класу. В Python __init__ виконує перевантаження операції створення екземплярів. Другий метод __sub__ реалізує операцію віднімання.>>>class Перевантаження: #створюється новий класdef __init__(self, start):self.data = startdef __sub__(self, other): # примірник мінус otherreturn Перевантаження(self.data - other) #Результатом буде новий примірник>>>A = Перевантаження(10) #__init__(A, 10)>>>B = A – 2 #__sub__(B, 2)>>>B. data #B – це новий екземпляр класу Перевантаження8.Детальніше про метод __init__
Метод __init__ найчастіше використовується при роботі з класами. Він незамінний для ініціалізації різних об'єктів. __init__ окремо викликати. При створенні нового примірника метод автоматично отримує аргументи, зазначені в дужках.
За допомогою методів перевантаження можна реалізувати будь-які операції з вбудованими типами даних. Більшість використовуються тільки при вирішенні спеціальних завдань, в яких необхідно, щоб об'єкти імітували поведінку стандартних об'єктів.
Методи успадковуються від суперклассов і не є обов'язковими. На початкових етапах можна легко без них обійтися. Але для повного занурення в програмування і суть ООП потрібен навик роботи з операторами.
Метод __getitem__
Метод __getitem__ виконує перевантаження доступу до елемента з індексом. Якщо він успадковується або присутній у визначенні класу, то при кожній операції індексування інтерпретатор буде викликати його автоматично. Наприклад, коли примірник F з'являється у виразі вилучення елемента з індексом, такому як F[i], інтерпретатор Python викликає метод __getitem__, передає об'єкт F в першому аргументі індекс, вказаний у квадратних дужках, у другому.
Наступний клас «ПримерИндексации» повертає квадрат значення індексу:>>>class ПримерИндексации:def __getitem__(self, index):return index ** 2>>>F = ПримерИндексации ()>>>F[2] #Вираз F[i] викликає F.__getitem__(i)4>>>for i in range(5):print(F[i], end=« ») # Викликає __getitem__(F, i) в кожній ітерації0 1 4 9 16
З допомогою цього методу можна виконати операцію вилучення зрізу, до якої часто вдаються під час роботи з послідовностями. При обробці списків стандартний синтаксис операції виглядає наступним чином:>>>Список = [13, 6, «і», «з», 74,9]>>>Список[2:4][«та», «з»]>>>Список[1:][6, «і», «з», 74,9]>>>Список[:-1][13, 6, «і», «з»]>>>Список[::2][13, «і», 74,9]
Клас, що реалізує метод __getitem__:>>>class Індексатор:мой_список = [13, 6, «і», «з», 74,9]def __getitem__(self, індекс): #Викликається при індексуванні або витягу зрізуprint(«getitem: », індекс)return self.мой_список[індекс] #Виконує індексування або витягує зріз>>>X = Індексатор()>>>X[0] #Під час індексування __getitem__ отримує ціле числоgetitem: 013>>>X[2:4] # При витяганні зрізу __getitem__ отримує об'єкт зрізуgetitem: slice(2, 4, None)[«та», «з»]
Звернення до атрибутів
Для отримання посилання на атрибут використовується спеціальний метод __getattr__. Він викликається з ім'ям атрибута у вигляді рядка у випадках виявлення спроби отримати посилання на неіснуючий або невизначений атрибут. Коли інтерпретатор може виявити шуканий об'єкт в дереві спадкування, __getattr__.не викликається.
Метод зручний для узагальненої обробки запитів до атрибутів:>>>class Gone:def __getattr__(self, atname):if atname == «age»:return 20else:raise AttributeError, atname>>>D = Gone()>>>D. age20>>>D. nameAttributeError: name
У класу Gone та його примірника D своїх атрибутів немає. Тому при зверненні до D. age автоматично викликається метод __getattr__. Сам примірник передається як self, а ім'я невизначеного «age» в рядку atname. Клас повертає результат звернення до імені D. age, незважаючи на те, що даного атрибуту у нього немає.
Якщо класом не передбачається обробка атрибута, метод __getattr__ викликає вбудоване виняток, і тим самим передає інтерпретатору інформацію про те, що ім'я в дійсності є невизначеним. В даному випадку спроба звернутися до імені D. name призводить до появи помилки.
Аналогічним чином працює метод перевантаження операторів __setattr__, перехоплюючи кожну спробу привласнити значення атрибуту. Якщо цей метод прописаний в тілі класу, вираз «self.атрибут = значення» буде перетворено на виклик методу self.__setattr_(«атрибут», значення).
Ми описали лише декілька з існуючих методів перевантаження. Весь перелік знаходиться у стандартному керівництві мови і містить набагато більше імен.Додаткові можливості
ООП іноді використовують для складних і нестандартних завдань. Завдяки успадкування класів в Python, поведінка вбудованих типів даних та їх можливості піддаються розширенню та адаптації.
Якщо вас не влаштовує той факт, що індексація в послідовностях починається з нуля, ви можете це виправити за допомогою інструкції class. Для цього потрібно створити підклас типу list з новими іменами всіх типів і реалізувати необхідні зміни. Також в ООП на мові Python існують декоратори функцій, статичні методи і безліч інших складних і спеціальних прийомів.
Категория: Техника