Управления доступом к атрибутам в Python

Фактически это конспект 37 главы книги Марка Лутца "Изучаем Python"
Код на github

Управления доступом к атрибутам в Python может осуществляться несколькими способами:
  • __getattr__ __set__ __getattribute__ __delete__
  • property
  • дескриптор
property и дескрипторы применяются к отдельным атрибутам.

Свойства

attribute = property(fget, fset, fdel, doc)
Все аргументы по умолчанию None, при обращение приводят к исключению

Примеры

# пример использования property
# attribute = property(fget, fset, fdel, doc)


class Person:
    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self, name):
        self._name = name

    def del_name(self):
        print('del name', self.name)
        del self._name

    name = property(get_name, set_name, del_name)


class SubPerson(Person):
    pass


bob = Person('bob')
print(bob.name)
bob.name = 'Bobi'
print(bob.name)
del bob.name
print()
tod = Person('tod')
print(tod.name)
tod.name = 'Todi'
print(tod.name)
del tod.name

print('-' * 10)


# -------------------------------
# вычисляемые атрибуты
class PropSquare:
    def __init__(self, start):
        self.value = start

    def get_x(self):
        return self.value ** 2

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

    X = property(get_x, set_x)


P = PropSquare(3)
print('P = PropSquare(3)')
Q = PropSquare(32)
print('Q = PropSquare(32)')

print('P.X = ', P.X)
P.X = 4
print('P.X = 4')
print('P.X = ', P.X)
print('P.Q = ', Q.X)

print('-' * 10)


# -------------------------------
# декоратор
class PersonDec:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @name.deleter
    def name(self):
        print('deleter name', self.name)
        del self._name


bob = PersonDec('bob dec')
print(bob.name)
bob.name = 'Bobi Dec'
print(bob.name)
del bob.name


Дескрипторы

Протокол дескрипторов позволяет передавать выполнение операций чтения и записи для определенного атрибута методам отдельного обьекта класса.
В отличии от свойств в дескрипторе при отсутствии метода не возникает исключение.
Если не реализован __set__, то атрибут будет просто переопределен


class Descriptor:
    """
    self - экземпляр дескриптора
    instance - экземпляр клиенского класса
    owner - клиенский класс
    """

    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        pass

    def __delete__(self, instance):
        pass


# Name - вложенный дескриптор для класса Person
class Person:
    def __init__(self, name):
        self._name = name

    class Name:
        def __get__(self, instance, owner):
            return instance._name

        def __set__(self, instance, value):
            instance._name = value

        def __delete__(self, instance):
            print('delete name', instance._name)
            del instance._name

    name = Name()


bob = Person('bob dec')
print(bob.name)
bob.name = 'Bobi Dec'
print(bob.name)
del bob.name

print('-' * 10)


# Вычисляемые атрибуты
# Значения дескриптор хранит у себя
class DescSquare:
    def __init__(self, start):
        self.value = start

    def __get__(self, instance, owner):
        return self.value ** 2

    def __set__(self, instance, value):
        self.value = value


class Client1:
    X = DescSquare(2)
    Y = DescSquare(3)


class Client2:
    X = DescSquare(32)


c1 = Client1()
c2 = Client2()
print('c1.X = {0}, c1.Y = {1}'.format(c1.X, c1.Y))
print('c2.X = ', c2.X)
c1.Y = 10
print('c1.Y = 10')
print('c1.Y = ', c1.Y)

print('-' * 10)


# Простая имитация свойства
class Property:
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.__doc__ = doc

    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        if self.fget is None:
            raise AttributeError("can't get attribute")
        return self.fget(instance)  # аргумент self

    def __set__(self, instance, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(instance, value)

    def __delete__(self, instance):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(instance)


class Person:
    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self, value):
        self._name = value

    name = Property(get_name, set_name)


bob = Person('bob')
print(bob.name)
bob.name = 'Bobi'
print(bob.name)
# del bob.name    # delete не реализован. вызовит ошибку
print()
tod = Person('tod')
print(tod.name)
tod.name = 'Todi'
print(tod.name)

__getattr__ и __getattribute__

__getattr__ -вызывается при обращение к несуществующим атрибутам
__set__ -при присваивании значений
__getattribute__ -вызывается при обращение к любым атрибутам

Делегирования - управление доступом ко всем атрибутам встроенного объекта
функция getattr(self, item) эквивалентна self.item


class Wrapper:
    def __init__(self, object):
        self.wrapped = object

    def __getattr__(self, item):
        print('Trace:', item)
        return getattr(self.wrapped, item)


class Person:
    def __init__(self, lname, fname):
        self.lname = lname
        self.fname = fname

    def get_full_name(self):
        return self.lname + ' ' + self.fname


p = Person('Last', 'First')
wp = Wrapper(p)
print(wp.lname)
print(wp.fname)
print(wp.get_full_name())
# print(wp.test) # AttributeError

Предотвращение зацикливаний
Если внутри метода __getattribute__ обратится к другому атрибуту то эта операция произведет вызов метода  __getattribute__ и это приведи к бесконечному циклу


class Person:
    # X = 100

    def __init__(self, name):
        print('__init__')
        self._name = name  # вызов __setattr__

    def __getattr__(self, attr):  # вызов отсутствущих атрибутов
        print('__getattr__')
        if attr == 'name':
            return self._name
        else:
            raise AttributeError(attr)

    def __getattribute__(self, attr):
        print('__getattribute__', self, attr)
        if attr == 'name':
            attr = '_name'
        return object.__getattribute__(self, attr)  # предотвращаем зацикливание

    def __setattr__(self, attr, value):
        print('__setattr__')
        if attr == 'name':
            attr = '_name'
        self.__dict__[attr] = value  # предотвращаем зацикливание

    def __delete__(self, attr):
        print('__delete__')
        if attr == 'name':
            attr = '_name'
        del self.__dict__[attr]  # предотвращаем зацикливание

p = Person('German')
print(p.name)

Комментарии

  1. Mens Titanium Wedding Rings - Etricki - TikTok
    Mens Titanium Wedding t fal titanium pan Rings Men who never got married need a quality wedding ring. It is made with a premium grade 23 titanium material micro touch titanium trim which is titanium engagement rings for her extremely high titanium anodizing quality.

    ОтветитьУдалить

Отправить комментарий

Популярные сообщения из этого блога

Асинхронное выполнение процедур или триггера в MS SQL Server

Рекурсивные SQL запросы

Кратко про SQLAlchemy Core