Геттери (Getters) та сеттери (Setters): керування атрибутами в Python
Використання геттерів та сеттерів для інкапсуляції даних — один із основних принципів об’єктно-орієнтованого програмування. Про те, що це таке та як працює, читайте у цьому матеріалі.
Зміст
Атрибути — що це та як використовуються
Оскільки геттери та сеттери, про які йтиметься нижче, працюють з атрибутами, давайте спочатку поговоримо про те, що таке атрибути та як вони використовуються.
У Python кожен об’єкт має свій набір атрибутів, які визначають його стан та поведінку. Атрибути можуть бути:
- змінними (даними, які зберігаються всередині об’єкта);
- методами (функціями, визначеними усередині класу).
Кожен атрибут має своє ім’я, яке можна використовувати для доступу до значення змінної або виклику методу.
Класичний приклад використання атрибутів у Python — клас Point
, який представляє геометричну точку на площині.
У цьому класі атрибутами виступають координати x
та y
, які можуть бути змінними, які зберігаються всередині об’єкта.
Ось як це виглядає у коді:
class Point: def __init__(self, x, y): self.x = x self.y = y
Ми створюємо та ініціалізуємо клас Point
із двома атрибутами x
та y
, тобто координатами точки. Атрибути встановлюються у конструкторі класу за допомогою методу __init__
.
Після цього ми можемо створити об’єкт класу Point
та використовувати його для представлення геометричної точки на площині. Наприклад:
p = Point(2, 3) print(p.x) # 2 print(p.y) # 3
У цьому прикладі ми створюємо об’єкт p
класу Point
і передаємо йому значення 2
та 3
у якості координат x
та y
відповідно. Потім ми використовуємо атрибути x
та y
об’єкта p
, щоб отримати значення його координат.
Атрибут (attribute) у програмуванні (змінна чи метод) відноситься до об’єкта. У Python все є об’єктом.
У тому числі й класи: вони є конструкторами об’єктів, тобто надають шаблон для створення екземплярів класу. Класи мають свої власні атрибути та методи, які можна використовувати для створення екземплярів цього класу.
Способи отримання та встановлення атрибутів
Є кілька способів отримання та встановлення атрибутів у Python.
Застосування точкової нотації
Це означає, що ми звертаємося до атрибута об’єкта, використовуючи точку та ім’я атрибута. Наприклад, якщо у нас є об’єкт Person
з атрибутом name
, ми можемо отримати його значення, використовуючи вираз Person.name
.
Приклад точкової нотації у коді:
class Animal: def __init__(self, name, species): self.name = name self.species = species class Cat(Animal): def __init__(self, name, color): super().__init__(name, species="Cat") self.color = color class Camel(Animal): def __init__(self, name, humps): super().__init__(name, species="Camel") self.humps = humps my_cat = Cat("Мартуня", "black") my_camel = Camel("Barmaley", 2) print(my_cat.name) # Output: Мартуня print(my_camel.humps) # Output: 2
У цьому прикладі є базовий клас Animal
, а також два підкласи: Cat
та Camel
. Кожен екземпляр класу Cat
має атрибути name
та color
, а кожен примірник класу Camel
— атрибути name
та humps
.
Ми можемо звернутися до атрибутів кожного екземпляра, використовуючи точкову нотацію, наприклад, my_cat.name
та my_camel.humps
.
Використання функцій getattr(), setattr() та delattr()
Ці функції дозволяють динамічно отримувати, встановлювати або видаляти атрибути об’єкта. Наприклад, ми можемо отримати значення атрибута об’єкта Person
за допомогою функції getattr(Person, "name")
, а потім встановити нове значення за допомогою функції setattr(Person, "name", "Alice")
.
Розглянемо, як це виглядає у коді.
Напишемо клас Car
, який матиме три атрибута: make
, model
та year
. Потім створимо об’єкт car1
класу Car
та встановимо його атрибути:
class Car: def __init__(self, make, model, year): self.make = make self.model = model self.year = year # Створюємо примірник класу car1 car1 = Car('Ford', 'Mustang', 2021) # Отримуємо значення атрибуту об'єкта car1 з допомогою getattr() make = getattr(car1, 'make') print(make) # 'Ford' # Змінюємо значення атрибуту об'єкта car1 з допомогою setattr() setattr(car1, 'year', 2022) year = getattr(car1, 'year') print(year) # 2022 # Видаляємо атрибут об'єкта car1 за допомогою delattr() delattr(car1, 'model') try: model = getattr(car1, 'model') except AttributeError as e: print(e) # 'Car' object has no attribute 'model'
За допомогою getattr()
ми отримуємо значення атрибута make
. Потім змінюємо значення атрибута year
за допомогою setattr()
та отримуємо його нове значення. І, нарешті, просто видаляємо атрибут model
за допомогою delattr()
.
Увага! При спробі отримати значення атрибута model
після видалення виникне виняток AttributeError
, який ми обробили у блоці try-except
.
Використання словників атрибутів
У Python об’єкти мають словник атрибутів, який містить пари «ключ-значення» для всіх атрибутів об’єкта. Ми можемо отримати доступ до словника атрибутів об’єкта за допомогою виразу obj.dict
. Ми також можемо встановити значення атрибута, додавши нову пару «ключ-значення» в словник.
Наприклад, давайте створимо об’єкт, який представлятиме сайт про технології HighloadToday:
class Website: def __init__(self, name, url): self.name = name self.url = url website = Website("HighloadToday", "https://highload.tech")
Ми можемо отримати доступ до словника атрибутів об’єкта Website
, використовуючи вираз website.dict
.
Для встановлення нових значень у словник атрибутів ми можемо просто додати нову пару «ключ-значення», наприклад:
website.__dict__["description"] = "The latest news and trends in highload technologies"
Тепер у словнику атрибутів об’єкта website
буде додано новий атрибут з ключем "description"
та значенням "The latest news and trends in high-load technologies"
.
У цьому можна переконатися, вивівши на екран словник:
class Website: def __init__(self, name, url): self.name = name self.url = url website = Website("HighloadToday", "https://highload.tech") website.__dict__["description"] = "The latest news and trends in highload technologies" print(website.__dict__)
На дисплей буде виведено пари «ключ-значення»:
{'name': 'HighloadToday', 'url': 'https://highload.tech', 'description': 'The latest news and trends in highload technologies'}
Визначення геттерів та сеттерів у програмуванні
У Python геттери та сеттери — це методи для керування доступом до атрибутів об’єктів класу, які забезпечують контроль за зміною або отриманням значень атрибутів.
Геттер в Python — це метод, який повертає значення атрибута класу об’єкта. Він зазвичай використовується для отримання значення приватного атрибута класу, який повинен бути прихований від змін класу ззовні.
Геттери в Python можуть бути реалізовані у вигляді методу, ім’я якого починається з префікса get_
. Наприклад, якщо ми хочемо створити геттер для атрибуту name
в класі, ми можемо визначити метод з ім’ям get_name
, який повертає значення атрибута name
.
Сеттер в Python — це метод, який встановлює значення атрибута об’єкта класу. Він зазвичай використовується для встановлення значення приватного атрибута класу, який має бути прихований від змін ззовні класу.
Сетери в Python можуть бути реалізовані у вигляді методу, ім’я якого починається з префікса set_
. Наприклад, якщо ми хочемо створити сеттер для атрибуту name
в класі, ми можемо визначити метод з ім’ям set_name
, який встановлює значення атрибута name
.
Реалізація інкапсуляції даних
Геттери та сеттери — один із способів реалізації інкапсуляції даних, найважливішої концепції в об’єктно-орієнтованому програмуванні (ООП).
Використання концепції інкапсуляції підвищує безпеку коду, а також дозволяє створити більш гнучкий та розширюваний код, покращує модульність та зменшує залежність між різними компонентами програми.
Суть інкапсуляції в тому, щоб приховати деталі реалізації об’єкта, забезпечивши при цьому доступ до нього через публічний інтерфейс.
В Python інкапсуляція реалізується через використання атрибутів з різним рівнем доступу. Атрибути можуть бути публічними , захищеними або приватними, залежно від того, яким чином вони оголошені:
- Публічні атрибути доступні для читання та запису з будь-якого місця у програмі. Вони оголошуються без використання подвійного підкреслення на початку імені, наприклад:
my_attribute
. - Захищені атрибути доступні лише зсередини класу та його нащадків. Вони оголошуються з використанням одного підкреслення на початку імені, наприклад:
_my_attribute
. - Приватні атрибути доступні лише всередині класу. Вони оголошуються з використанням подвійного підкреслення на початку імені, наприклад:
__my_attribute
.
Використання атрибутів з різним рівнем доступу дозволяє створювати безпечніший та надійніший код, оскільки обмежує доступ до важливих даних та методів лише необхідних ділянок коду.
Приклад використання декоратора property
У Python геттери та сеттери можна визначити як явно, використовуючи методи геттера та сеттера, так й через використання декораторів (property
). У простих випадках краще використовувати декоратори, щоб скоротити код та зробити його більш читабельним.
Припустимо, ми хочемо, щоб у класі Person
при встановленні віку (age
) значення, що виходять за межі допустимого діапазону (від 0 до 150 років), обрізалися до відповідних крайніх значень. Крім того, ми хочемо, щоб при отриманні віку значення повернули в роках, а при установці — в місяцях.
Пишемо такий код:
class Person: def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age // 12 # повертаємо вік в роках @age.setter def age(self, value): if value < 0: value = 0 elif value > 150: value = 150 self._age = value * 12 # перераховуємо місяці в року
У коді ми визначаємо геттер age
, який повертає вік у роках (self._age // 12
) та сеттер age
, який встановлює значення віку у місяцях (self._age = value * 12
). При цьому перед встановленням нового значення віку в місяцях ми перевіряємо, чи не виходить воно за межі допустимого діапазону (0-150 років), і при необхідності обрізаємо значення до крайніх значень (0 або 150).
Дескриптори в Python
Дескриптори в Python — це механізм, який дозволяє визначати, як атрибути повинні працювати всередині класів.
Це спеціальні об’єкти, які можуть бути приєднані до атрибутів класу для контролю доступу до них. Їх можна використовувати для реалізації різних патернів проєктування, таких як властивості (properties
), методи, класи, статичні методи тощо.
В основному дескриптори дозволяють перевизначити, як працюють операції доступу до атрибутів, такі як читання, запис та видалення.
Дескриптори в Python використовуються в багатьох вбудованих класах та бібліотеках, наприклад, у класі property
, який використовується для створення геттерів та сеттерів, а також у бібліотеці functools
, яка містить декоратори для зміни поведінки функцій.
Приклад використання дескриптора property
Приклад використання дескриптора property
в Python для реалізації геттера та сеттера для атрибута класу Celsius
:
class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 def get_temperature(self): return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("Temperature below -273 is not possible") self._temperature = value temperature = property(get_temperature, set_temperature) class Temperature: temperature = Celsius() def __init__(self, initial_temp): self.temperature.temperature = initial_temp def to_fahrenheit(self): return self.temperature.to_fahrenheit()
Цей код дозволяє працювати з температурами в градусах Цельсія та переводити значення температури з однієї шкали до іншої.
Він визначає два класи: Celsius
та Temperature
. Клас Celsius
описує температурну шкалу Цельсія та визначає атрибут temperature
, а також функції get_temperature()
, set_temperature()
та to_fahrenheit()
.
Дескриптор property
використовується для визначення геттера get_temperature
та сеттера set_temperature
. У сеттері ми додаємо перевірку на те, що температура не може бути нижчою від абсолютного нуля (−273 градуси Цельсія). Функція to_fahrenheit()
перетворює температуру в градусах Цельсія на градуси Фаренгейта.
Клас Temperature
містить об’єкт класу Celsius
та визначає функції __init__()
та to_fahrenheit()
. Функція __init__()
ініціалізує значення температури за допомогою методу temperature
об’єкта Celsius
, а функція to_fahrenheit()
— перетворює температуру в градусах Цельсія на градуси Фаренгейта за допомогою методу to_fahrenheit()
об’єкта Celsius
.
Управління доступом до даних
Геттери та сеттери можуть використовуватися для керування доступом до даних у класах Python. Наприклад, якщо ви хочете обмежити доступ до певних атрибутів об’єкта, ви можете використовувати геттери та сеттери для встановлення та отримання значень цих атрибутів.
Розглянемо, наприклад, ситуацію, коли ми маємо клас BankAccount
із конфіденційною інформацією про баланс рахунку. Ми хочемо обмежити доступ до цієї інформації для всіх, крім власника рахунку та банківського працівника, який має доступ до інформації щодо роботи.
Ми можемо використовувати геттери та сеттери для реалізації цього захисту. Для цього ми створимо приватну змінну __balance
всередині класу та створимо геттер та сеттер для доступу до неї:
class BankAccount: def __init__(self, owner, balance): self.__owner = owner self.__balance = balance def get_balance(self, user): if user == self.__owner or user == "bank_employee": return self.__balance else: raise ValueError("Access denied") def set_balance(self, user, new_balance): if user == "bank_employee": self.__balance = new_balance else: raise ValueError("Access denied")
Створюємо клас BankAccount
. У конструкторі __init__
визначаємо два приватні атрибути — __owner
та __balance
.
Зверніть увагу! Назва змінних починається з подвійного підкреслення. Це означає, що вони приватні та захищені від змін ззовні.
Далі визначаємо метод get_balance
, який повертає значення балансу облікового запису лише в тому випадку, якщо людина, яка запитує баланс — власник облікового запису або банківський співробітник.
Визначаємо метод set_balance
, який встановлює нове значення балансу облікового запису лише тоді, коли користувач, який намагається змінити баланс, є банківським співробітником.
Якщо користувач, який намагається виконати будь-яку операцію, не відповідає умовам, заданим у методах get_balance
та set_balance
, то генерується виняток ValueError
із повідомленням "Access denied"
.
Іншими словами, за допомогою геттера та сеттера надається доступ до приватного атрибуту __balance
лише обраним користувачам, що робить наш код більш безпечним та захищеним від несанкціонованого доступу.
Використання геттерів та сеттерів для кешування
Розглянемо такий код на Python:
class Cache: def __init__(self): self._cache = None @property def data(self): # Якщо кеш не порожній, то повертаємо його значення if self._cache is not None: print("Повертаємо дані із кеша") return self._cache # Інакше читаємо дані з бази даних і зберігаємо їх в кеші print("Читаємо дані з бази даних") data = "some data from database" self._cache = data return data @data.setter def data(self, value): # Видаляємо значення кеша при встановленні нового значення print("Видаляємо дані із кешу") self._cache = None
Тут є клас Cache
з атрибутом _cache
, який використовується для зберігання кешованих даних:
- якщо кеш не порожній, при зверненні до властивості
data
повертається значення з кешу; - інакше — читаються дані з бази даних та зберігаються в кеші.
При встановленні нового значення через сеттер data
значення кеша видаляється, щоб при наступному зверненні дані знову були прочитані з бази даних:
cache = Cache() # Перший виклик властивості data, дані будуть прочитані з бази даних print(cache.data) # Повторний виклик властивості data, дані будуть взяті із кешу print(cache.data) # Змінюємо значення властивості data, при наступному виклику дані будуть прочитані з бази даних cache.data = "new data" # Наступний виклик властивості data, дані будуть прочитані з бази даних print(cache.data)
У цьому прикладі, при першому зверненні до властивості data
дані будуть прочитані з бази даних та збережені в кеші. При наступних зверненнях до властивості data
дані будуть взяті з кешу, без звернення до бази даних.
При зміні значення властивості data
значення кеша буде видалено, тож при наступному зверненні до властивості data
дані знову будуть прочитані з бази даних.
Обмеження доступу до даних віддаленого API
Припустимо, що є програма, яка отримує дані з віддаленого API. Замість того, щоб безпосередньо звертатися до методів API, можна створити клас-обгортку, який використовуватиме геттери та сеттери для отримання та встановлення даних з API.
Код такого завдання можна представити приблизно так:
import requests class APIWrapper: def __init__(self, base_url, api_key): self.base_url = base_url self.api_key = api_key @property def users(self): url = f"{self.base_url}/users" headers = {"Authorization": f"Bearer {self.api_key}"} response = requests.get(url, headers=headers) return response.json() @users.setter def users(self, data): url = f"{self.base_url}/users" headers = {"Authorization": f"Bearer {self.api_key}"} response = requests.post(url, headers=headers, json=data) return response.json()
Створюється клас-обгортка для доступу до віддаленого API. У нього є два методи:
users
, який повертає список користувачів з API;users.setter
, який надсилає дані на сервер для створення нового користувача.
При цьому для доступу до даних з API використовуються геттери та сеттери. Щоб отримати список користувачів з API, можна написати такий код:
wrapper = APIWrapper("https://api.example.com", "API_KEY") users = wrapper.users
А створити нового користувача можна так:
new_user = {"name": "Bondarenko Serge", "email": "[email protected]"} wrapper.users = new_user
Висновок
Використання геттерів та сеттерів можна розглядати як гарну звичку при написанні класів у Python. Забезпечуючи контроль доступу до змінних об’єктів, ви підвищуєте безпеку вашого коду та дозволяєте змінювати логіку роботи програми.
Правильне використання геттерів та сеттерів може посилити функціональність вашого коду та зробити його більш надійним.
Favbet Tech – це ІТ-компанія зі 100% українською ДНК, що створює досконалі сервіси для iGaming і Betting з використанням передових технологій та надає доступ до них. Favbet Tech розробляє інноваційне програмне забезпечення через складну багатокомпонентну платформу, яка здатна витримувати величезні навантаження та створювати унікальний досвід для гравців.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: