Использование закрытых переменных в дочерних классах

291 просмотра
0
0 Комментариев

Как обращаться к закрытым переменным в дочерних классах?

Вот код:

class Animal(object):
    def __init__(self,age):
        self.age = age
        self.__type = 'animal'
 
    def __show(self):
        print('meow')
 
    def show1(self):
        self.__show()
 
class Cat(Animal):
    def __init__(self,age):
        super().__init__(age)
 
 
 
    def show_type(self):
        print(self.__type)
 
 
Kitty = Cat(3)
Kitty.show1() #работает нормально - печатает meow, как и должен
Kitty.show_type() #не работает ругается: AttributeError: 'Cat' object has no attribute '_Cat__type'

Как сделать правильно, чтобы была возможность обращаться к закрытым переменным через дочерние классы?


Добавить комментарий

3 Answers

Python Опубликовано 14.12.2018
0

Как обращаться к закрытым переменным в дочерних классах?

Правильный ответ: вы НЕ должны обращаться к закрытым (__) атрибутам других классов.

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

Также программисты, пришедшие из языков с определённой моделью ООП (С++, Java), которая делает большой акцент на видимость атрибутов (private, protected, public), могут злоупотреблять __ в Питоне. Возможно __ были лишними с самого начала.

Наконец, наихудшее решение это узнать схему искажения имён, которую использует ваша версия Питона и руками создать соответствующие имена для доступа:

>>> class C:
...     def __init__(self):
...         self.__x = 'x'
...
>>> c = C()
>>> c.__x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute '__x'
>>> import dis
>>> dis.dis(C.__init__)
  3           0 LOAD_CONST               1 ('x')
              3 LOAD_FAST                0 (self)
              6 STORE_ATTR               0 (_C__x)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> c._C__x
'x'

Изучение исходников _Py_Mangle() обнаружило интересную особенность: начальные символы подчёркивания убираются из имени класса, то есть имя _C__x остаётся прежним для классов C, _C, __C и т.д.

Добавить комментарий
0
  1. Если переменная предваряется ‘__’, то в результате получается новое поле с указанием имени класса. В вашем случае это _Animal_type. Сделано это для того, чтобы избежать именн коллизий в дочерних и базовом класах, поэтому п.2
  2. Прежде чем создавать доступ из дочерних нужно подумать над 2 вопросами «А для чего я это поле создавал с двумя подчеркиваниями?» и «От чего я хотел защититься?»
  3. По факту в Python-е нету открытых\защищенных\закрытых переменных и методов. Python-программисты указывают другим программистам предостережение по использования переменных и методов одним подчеркиваниям. Предваряя одним подчеркиванием они говорят тем самым «Я в любой момент могу изменить этот код без уведомления об этом в справке и буду прав! Пользуйтесь на свой страх и риск»
Добавить комментарий
0

Быстрый ответ назвал @zed в своём комментарии:
для доступа к полю, названному с двумя подчёркиваниями, можно использовать его действительное имя, которое в СPython получается следующим образом: _, т.е. в вашем случае это будет строка _Animal__type.

Другое дело, что этот способ не совсем подходит для вашего случая.


В вашем случае будет правильнее использовать название поля с одним подчёркиванием (если его нужно скрывать), так как это поле должно быть доступно для классов-наследников. В таком случае методы будут работать правильно:

class Animal(object):
    def __init__(self,age):
        self.age = age
        self._type = 'animal'
 
    def __show(self):
        print('meow')
 
    def show1(self):
        self.__show()
 
 
class Cat(Animal):
    def __init__(self,age):
        super().__init__(age)
 
    def show_type(self):
        print(self._type)
 
 
Kitty = Cat(3)
Kitty.show1()        # meow
Kitty.show_type()    # animal

Поля, имеющие в названии два подчёркивания (не являющиеся специальными), имеют довольно узкую область применения, поэтому в большинстве случаев достаточно использования одного подчёркивания в случае, если поле не желательно использовать извне класса или использование имени без подчёркивания, если поле безопасно использовать в любом месте (можно использовать декоратор @property для создания read-only полей).

Добавить комментарий
Напишите свой ответ на данный вопрос.
Scroll Up