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

в 11:25, , рубрики: class, python, ооп, Песочница, метки: , ,

В этой заметке я хотел бы привести краткое описание методов регулирования доступа к атрибутам класса в питоне с помощью декораторов и с помощью присвоения специальным образом имен атрибутам в соответсвии с хорошим стилем программирования, описанным в PEP 8 . Статья написана на основе обсуждений данной темы на StackOverflow.com, нескольких мануалов и личного опыта автора.

Занимаясь написанием программ, выполняющие научные расчеты, я перешел с c++ на питон, чтобы использовать всю мощь библиотек, к которым относятся numpy, scipy, matplotlib, pyquante и прочие, распространяющиеся под свободной лицензией и находящихся в избытке на github. Однако, на начальном этапе такого перехода у меня возник дискомфорт, связанный с отсутствием таких привычных в с++ модификаторов доступа, как privat, public и protected, а также методов get() и set(), что и побудило меня написать эту заметку. На StackOverflow первое, что бросается в глаза при обсуждении отсутствия модификаторов доступа в питоне, — это всеобщее согласие с большой долей ответственности разработчика при написании кода, которая побуждает его соблюдать хороший стиль программирования, описанный в PEP 8 . Проблема заключается в том, что в питоне всегда можно получить доступ к любому атрибуту любого класса, однако, согласно хорошему стилю, такой доступ пользователями библиотеки классов должен быть осуществлен не прямо, а с помощью некоторого интерфейса и только там, где это нужно и предусмотрено разработчиком.

Рассмотрим класс, содержащий атрибут attr:

class MyClass:
    def __init__(self):
        self.attr = 0

Существует очевидная возможность доступа к атрибуту напрямую после создания объекта класса:

a=Class()
a.attr

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

class MyClass:
    def __init__(self):
        self._attr = 0

Конечно, в этом случае к атрибуту можно обратиться извне класса как:

a._attr

Но каждый раз видя этот код вы будете знать, что: а) осуществляется обращения к атрибуту объекта а и б) ваш код не соответствует хорошему стилю программирования, поскольку атрибут этого класса с таким именем не предусматривает, что к нему будут обращаться вне определения класса.

Если все же есть необходимость предоставить пользователю доступ к атрибуту данного класса, лучше использовать декоратор @property. Например:

class MyClass:
    def __init__(self):
        self.attr = 0

    @property
    def attr(self):
        return self._attr

Теперь обращение к атрибуту извне класса может быть осуществлено привычным нам способом:

a.attr

В функцию attr(self) можно вложить дополнительную функциональность, обрабатывая определенным образом данные. Например можно сделать так, чтобы она возвращала только целую часть числа, модуль и т. д. В эту функцию можно также передать аргумент, с помощью которого управлять выводом значения self._attr, получая желаемый результат. Обрабатывая список аргументов этой функции, можно сделать что-то вроде ее перегрузки. Например, если переменная self._attr представлена массивом, то можно реализовать эту функцию так, что в случае обращения в виде a.attr мы получим весь массив, а в случае обращения a.attr(j) — один из его элементов.

Отметим важную деталь, что в данном случае у нас нет никакой возможности изменить значение переменно self._attr извне определения класса, не нарушая при этом хорошего стиля программирования. Другими словами, мы можем это сделать так:

a._attr = 0

Но не можем так:

a.attr = 0

Чтобы вторая опция стала доступной, существует другой декоратор @setter. Ниже приведен пример его использования:

class MyClass:
    def __init__(self):
        self.attr = 0

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self,value):
        self._attr = value

Вторым способом имитации модификатора доступа privat является присвоение имен атрибутам, начинающихся двойным подчеркиванием self.__attr (но не заканчивающихся! — такие имена, как правило, зарезервированы системой). В этом случае будет задействован так называемый name mangling, который заключается в том, что интерпретатор питона автоматически переименовывает атрибут self.__attr в self._classname__attr, где classname — имя текущего класса. В этом случае нет возможности обратиться к атрибуту даже таким варварским методом, как а.__attr. Внутри определения класса этот атрибут можно вызвать просто как self.__attr. Для осуществления доступа к таким именам извне часто используют встроенные функции __getattr__() , __setattr__() и зарезервированную переменную __dict__.

Описанные выше методы дают очень широкий спектр возможностей для управления доступом к атрибутам класса, вполне восполняющий отсутствие специальных модификаторов.

Автор: freude

Источник

Поделиться

* - обязательные к заполнению поля