def private(attribute_name):
def decorator(cls):
private_attribute = f"_{cls.__name__}__{attribute_name}"
def getter(self):
return getattr(self, private_attribute)
def setter(self, value):
setattr(self, private_attribute, value)
setattr(cls, attribute_name, property(getter, setter))
return cls
return decorator
@private("my_attribute")
class MyClass:
def __init__(self, my_attribute):
self._MyClass__my_attribute = my_attribute
obj = MyClass(10)
print(obj.my_attribute) # Вывод: 10
obj.my_attribute = 20
print(obj.my_attribute) # Вывод: 20
Здесь декоратор `private` переименовывает атрибут, делая его "приватным" (convention of name mangling), и создает геттер/сеттер для доступа через имя атрибута.
Инкапсуляция - это сокрытие внутреннего состояния и реализации объекта от внешнего мира, предоставляя контролируемый интерфейс для взаимодействия. В Python, хотя и нет строгой приватности, как в некоторых других языках (например, Java), инкапсуляцию можно эффективно эмулировать с помощью соглашений об именах (одинарное и двойное подчеркивание) и декораторов.
Вот как можно реализовать инкапсуляцию с использованием декораторов:
import functools
def private(attribute_name):
"""
Декоратор для создания "приватного" атрибута.
Использует геттеры и сеттеры, чтобы контролировать доступ.
"""
def getter(self):
return getattr(self, f"_{self.__class__.__name__}__{attribute_name}")
def setter(self, value):
setattr(self, f"_{self.__class__.__name__}__{attribute_name}", value)
def decorator(cls):
"""
Декоратор класса для применения геттеров и сеттеров.
"""
# Добавляем "приватный" атрибут с префиксом, чтобы избежать конфликтов
# с именами существующих атрибутов.
# Префикс состоит из двойного подчеркивания и имени класса.
setattr(cls, f"_{cls.__name__}__{attribute_name}", None)
# Создаем свойство (property) для управления доступом.
setattr(cls, attribute_name, property(getter, setter))
return cls
return decorator
# Пример использования:
@private("age")
class Person:
def __init__(self, name, age):
self.name = name
self.age = age # Используется сеттер, определенный декоратором
def introduce(self):
print(f"My name is {self.name} and I am {self.age} years old.")
# Альтернативный вариант: Декоратор можно применить к уже существующему классу
# class Person:
# def __init__(self, name, age):
# self.name = name
# self._Person__age = age # Используется для прямого доступа к "приватному" атрибуту
#
# def introduce(self):
# print(f"My name is {self.name} and I am {self.age} years old.")
#
# Person = private("age")(Person)
try:
person = Person("Alice", 30)
person.introduce() # My name is Alice and I am 30 years old.
print(person.age) # 30 (используется геттер)
person.age = 35 # Используется сеттер
print(person.age) # 35
# Внешний доступ к "_Person__age" все еще возможен, но это подразумевается как нарушение инкапсуляции.
print(person._Person__age) # 35
except Exception as e:
print(f"Error: {e}")
Объяснение:
@private(attribute_name)
принимает имя атрибута, который нужно инкапсулировать.private
определяются getter
и setter
. Эти функции возвращают и устанавливают значение атрибута соответственно, но с использованием имени, искажённого механизмом name mangling Python (добавлением _ClassName__
к имени атрибута).decorator(cls)
добавляет "приватный" атрибут с префиксом, чтобы избежать конфликтов с именами существующих атрибутов, а также создает свойство (property
) для управления доступом к этому атрибуту через getter
и setter
. property
позволяет обращаться к атрибуту как к обычному атрибуту, но при этом вызываются геттер и сеттер.Person.age
теперь становится свойством (property), которое управляется декоратором. person.age = 35
вызывает setter
, а print(person.age)
вызывает getter
._Person__age
) предотвращает случайный доступ к "приватному" атрибуту извне класса. Python переименовывает атрибуты, начинающиеся с двойного подчеркивания (но не заканчивающиеся им) во время выполнения. Хотя прямой доступ все еще возможен через искаженное имя, это считается нарушением соглашения об инкапсуляции.Преимущества:
Недостатки:
Этот подход позволяет реализовать инкапсуляцию на уровне соглашений и обеспечивает больший контроль над доступом к атрибутам класса, чем простое использование одинарного или двойного подчеркивания в именах атрибутов.