Menu
CoddyTech

Приватные атрибуты

Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 15 из 64.

Приватные атрибуты используют символы подчёркивания, чтобы показать, что к определённым данным не следует обращаться напрямую извне класса.

Зачем нужны приватные атрибуты? Представь, что у тебя есть банковский счёт. Ты не хочешь, чтобы кто-то мог напрямую изменить твой баланс — изменения должны проходить через надлежащие каналы (методы внесения и снятия средств), чтобы банк мог проверить транзакцию, выявить мошенничество, вести учёт и так далее. Приватные атрибуты работают так же: они защищают данные от неправильного изменения.

Вот пример с использованием одиночного символа подчеркивания (соглашение для «внутреннего использования»):

class Person:
    def __init__(self, name, age):
        self._name = name    # "protected" — для внутреннего использования
        self._age = age      # "protected" — для внутреннего использования

К атрибутам с одним символом подчёркивания всё ещё можно обращаться, но это сигнал, что так делать не следует:

person = Person("Alice", 30)
print(person._name)  # Работает, но не рекомендуется

Используйте двойные подчёркивания для более строгой защиты данных (искажения имён):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "private" — имя преобразуется
        self.__age = age     # "private" — имя преобразуется
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

Зачем использовать методы-геттеры и сеттеры? Они как привратники. Вместо того чтобы позволять кому угодно напрямую изменять __age (что может привести к отрицательному возрасту или другим недопустимым данным), мы заставляем использовать set_age(), который сначала проверяет введённое значение. Это предотвращает ошибки и обеспечивает целостность данных.

У «дополнительного кода» есть своё предназначение:

  • get_name() — контролируемый доступ для чтения имени
  • set_age(age) — проверяет возраст, прежде чем разрешить его изменить (никакого отрицательного возраста!)
  • Без этих методов кто-нибудь может установить age = -100 и сломать вашу программу

Используй методы доступа для работы с приватными атрибутами:

person = Person("Bob", 25)
print(person.get_name())  # Bob

person.set_age(30)        # Допустимо: age становится равным 30
person.set_age(-5)        # Недопустимо: age должен быть положительным! (age остаётся равным 30)

Видишь, в чём польза? Метод set_age() предотвращает появление недопустимых данных. Без него ты можешь случайно создать человека с возрастом -5, что не имеет смысла!

Атрибуты с двойным подчёркиванием подвергаются «искажению имени», но к ним всё ещё можно обращаться:

person = Person("Charlie", 35)
# Это не работает:
# print(person.__name)  # AttributeError

# Но это работает (не рекомендуется):
print(person._Person__name)  # Charlie

Пример из реальной жизни — зачем нужен весь этот код?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Приватный атрибут: нельзя изменить напрямую
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# Без приватных атрибутов:
# account.__balance = -1000000  # Катастрофа! Отрицательный баланс допустим

# С приватными атрибутами:
account = BankAccount(100)
account.deposit(50)           # Безопасно: значение проверено
print(account.get_balance())  # 150

«Лишний код» (методы) защищает ваши данные от недопустимых изменений. Это похоже на охрану, которая присматривает за вашими ценностями, вместо того чтобы оставлять их без защиты.

Вывод:

Alice
Bob
Age must be positive!
Charlie
150

Одинарное подчёркивание _attribute по соглашению означает «только для внутреннего использования». Двойное подчёркивание __attribute запускает искажение имени для большей приватности. Используй методы-геттеры и методы-сеттеры (методы доступа), чтобы правильно взаимодействовать с приватными данными и добавлять проверку. «Дополнительный код» предотвращает ошибки, гарантируя, что данные всегда действительны.

challenge icon

Задание

Легко

Заверши класс User в файле user.py, добавив функциональность для работы с приватным паролем, а затем используй его в файле driver.py. Следуй комментариям TODO, чтобы реализовать методы проверки и смены пароля.

Попробуйте сами

# TODO: Импортировать класс User из модуля user

# TODO: Получить тестовый случай из ввода
test_case = input()

if test_case == "constructor_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    # TODO: Проверить метод check_password с правильным паролем
    #       Вывести "Password check successful!", если он возвращает True
    #       Вывести "Password check failed!", если он возвращает False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    # TODO: Проверить метод check_password с неправильным паролем "wrong"
    #       Вывести "Incorrect password rejected!", если он возвращает False
    #       Вывести "Security issue: incorrect password accepted!", если он возвращает True
    result = user.check_password("wrong")
    if not result:
        print("Incorrect password rejected!")
    else:
        print("Security issue: incorrect password accepted!")

elif test_case == "change_password_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    # TODO: Проверить смену пароля с "secure123" на "newpass456"
    #       Вывести "Password changed successfully!", если он возвращает True
    #       Вывести "Password change failed!", если он возвращает False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: Проверить новый пароль с помощью "newpass456"
    #       Вывести "New password works!", если он возвращает True
    #       Выведите "New password doesn't work!", если метод возвращает False
    if user.check_password("newpass456"):
        print("New password works!")
    else:
        print("New password doesn't work!")

elif test_case == "change_password_wrong_old_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    # TODO: Попробуйте изменить пароль, указав неверный старый пароль
    #       Вывести "Security working: incorrect old password rejected!", если он возвращает False
    #       Выведите "Security issue: password changed with wrong old password!", если метод возвращает True
    result = user.change_password("wrong", "hackerpw")
    if not result:
        print("Security working: incorrect old password rejected!")
    else:
        print("Security issue: password changed with wrong old password!")

elif test_case == "comprehensive_test":
    # TODO: Создать объект User с начальным паролем "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: Проверить метод check_password с правильным паролем
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: Проверьте метод check_password с неверным паролем
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: Проверить смену пароля с "secure123" на "newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: Убедитесь, что старый пароль больше не действует
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: Убедитесь, что новый пароль действует
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: Попробуйте изменить пароль, указав неверный старый пароль
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: Убедитесь, что пароль остался защищённым после неудачной попытки изменения
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Python