Инкапсуляция

В python НЕТ возможности полностью ограничить доступ к переменным объекта и класса

Если вы изучали другие ООП-языки, то можете считать, что все поля в питоне public, а все методы virtual.

Еще раз об __name

mangling - изменение имен, чтобы имена имели принадлежность к классу. Это делается не столько для ограничения доступа к именам, а чтобы не было конфликта имен переменных в разных классах.

Имена внутри инструкции class, которые начинаются с __, но не заканчиваются на __ автоматически расширяются именем класса.

Зачем нужно? Делаем класс С3, который наследует классы С1 и С2. В обоих классах есть атрибут х:

class C1:
    def meth1(self): self.X = 88    # Предполагается, что X - это мой атрибут
    def meth2(self): print(self.X)
class C2:
    def meth1(self): self.X = 99    # и мой тоже
    def meth2(self): print(self.X)
class C3(C1, C2): 
    pass

I = C3() # У меня только один атрибут X!

Значение self.X зависит от того, кто последний присвоил значение в этот атрибут, ибо он один.

Используем псевдочастотные имена для предотвращения конфликтов:

class C1:
    def meth1(self): self.__X = 88      # Теперь X - мой атрибут
    def meth2(self): print(self.__X)    # Превратится в _C1__X
class C2:
    def metha(self): self.__X = 99      # И мой тоже
    def methb(self): print(self.__X)    # Превратится в _C2__X
class C3(C1, C2): pass

I = C3()                                # В I два имени __X (с именами классов)
I.meth1(); I.metha()
print(I.__dict__)
I.meth2(); I.methb()

Внимание: псевдочастотные имена мы создаем еще при разработке классов С1 и С2. При создании класса С3 мы пользуемся результатом правильного создания. Позволяет избежать случайных конфликтов.

@property - определяем get, set, del функции

Хочется read-only атрибуты.

Хочется, чтобы set методы для атрибута обязательно вызывались, запретить прямое присваивание obj.x = 7.

property([fget[, fset[, fdel[, doc]]]]) -> property

  • fget : Функция, реализующая возврат значения свойства.
  • fset : Функция, реализующая установку значения свойства.
  • fdel : Функция, реализующая удаление значения свойства.
  • doc : Строка документации для создаваемого свойства. Если не задано , будет использовано описание от fget (если оно существует).

Позволяет использовать методы в качестве свойств объектов — порождает дескриптор, позволяющий создавать «вычисляемые» свойства (тип property).

Пример использования в классическом виде:

class Mine(object):

    def __init__(self):
        self._x = None

    def get_x(self):
        return self._x

    def set_x(self, value):
        self._x = value

    def del_x(self):
        self._x = 'No more'

    x = property(get_x, set_x, del_x, 'Это свойство x.')

type(Mine.x)  # property
mine = Mine()
mine.x        # None
mine.x = 3
mine.x        # 3
del mine.x  
mine.x        # No more

Используя функцию в качестве декоратора можно легко создавать вычисляемые свойства только для чтения:

class Mine(object):

    def __init__(self):
        self._x = 'some value'

    @property
    def prop(self):
        return self._x

mine = Mine()
mine.prop                  # some value
mine.prop = 'other value'  # AttributeError

del mine.prop              # AttributeError

Объект свойства также предоставляет методы getter, setter, deleter, которые можно использовать в качестве декораторов для указания функций реализующих получение, установку и удаление свойства соответственно. Следующий код эквивалентен коду из первого примера:

class Mine(object):

    def __init__(self):
        self._x = None

    x = property()

    @x.getter
    def x(self):
        """Это свойство x."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        self._x = 'No more'

__slots__

Можно запретить создание атрибута объекта используя __slots__

class Robot():
  __slots__ = ['a', '_b', '__c']
  def __init__(self):
    self.a = 123
    self._b = 123
    self.__c = 123

obj = Robot()
print(obj._Robot__c)    # 123 - все еще можем доступиться до атрибута по полному имени
obj.__c = 77            # УРА! AttributeError: 'Robot' object has no attribute '__c'
print(obj.a)
print(obj._b)
print(obj.__c)

Нельзя создать атрибут объекта, не перечисленный в __slots__

Переопределение __setattr__

class Robot(object):
   def __init__(self):
      self.a = 123
      self._b = 123

   def __setattr__(self, name, val):
      if name not in ('a', '_b'):
            raise AttributeError(name)
      super().__setattr__(name, val)

obj = Robot()
obj.a = 5
print(obj.a)
obj.__c = 77       # AttributeError
print(obj.__c)     # AttributeError

Аналогично можно переопределить другие функции доступа к атрибутам.

results matching ""

    No results matching ""