#python #inheritance #name-mangling
#python #наследование #искажение имени
Вопрос:
Я столкнулся с неожиданным поведением при попытке протестировать некоторый код, где я пытался получить доступ к искаженному имени атрибута дочернего класса (__var). Вместо того, чтобы получать значение дочернего элемента, я думал, что вместо этого получаю __var родительского класса.
Окончательное редактирование: дочерний объект обращается к своей собственной версии переменной, имя которой искажено именем родителя вместо своего собственного. Теперь понятно ранее неожиданное поведение.
class test_parent(object):
__var = 1
def getvar(self):
a = self.__class__.__var
return a
class test_child(test_parent):
__var = 2
a = test_parent()
b = test_child()
b.__class__._test_parent__var = 3
print(a.getvar()) # Prints 1, as expected
print(b.__class__._test_child__var) # Prints 2, as expected
print(b.__class__._test_parent__var) # Prints 3, as expected
print(b.getvar()) # Prints 3
Комментарии:
1. Если бы произошло то, что вы ожидали, это разрушило бы всю цель искажения имени. Весь смысл искажения имени заключается в том, чтобы этого не произошло. В противном случае вы бы просто использовали обычное имя атрибута.
2. Весь смысл искажения имени заключается в том, чтобы убедиться, что, когда суперкласс и подкласс оба хотят использовать имя
__var
, эти варианты использования не имеют ничего общего друг с другом и вообще не влияют друг на друга. Влияние подкласса__var
на код суперкласса приведет к поражению этой цели.3. @jrmylow потому что это и есть цель искажения имен . Это для предотвращения конфликтов имен в подклассах . Просто не используйте искажение имени с двойным подчеркиванием, если вы не хотите такого поведения, потому что, опять же, в этом вся его цель.
4.
self.__var
будет ссылаться на__<class>_var
of<class>
, внутри которого оно было лексически определено. Его не волнует типself
или то, как он относится к наследованию.5.@geckos нет, идентификатор
self
не имеет значения, и когда вы вызываетеchild_instance.getvar()
self
, это фактическиChild
экземпляр. Важно то, определен ли метод в родительском или дочернем классе *. Не все, чтоself
есть, потому что, опять же,self
будет соответствовать экземпляру
Ответ №1:
Ответил с помощью комментариев:
Искажение имени Python происходит, когда интерпретатор «читает» метод (возможно, неверная терминология), а не при его вызове. Тесты подтверждают процесс, который происходит в фоновом режиме.
Когда __var встречается впервые, он находится внутри тела test_parent и искажается именем:
class test_parent(object):
__var = 1
становится:
class test_parent(object):
_test_parent__var = 1
Аналогичная вещь происходит, когда __var
встречается в test_child
, становление _test_child__var
. Ранее неожиданное поведение возникло из-за того, что то же самое произошло внутри getvar .
class test_parent(object):
...
def getvar(self):
...
a = self.__class__.__var
становится:
class test_parent(object):
...
def getvar(self):
...
a = self.__class__._test__parent_var
Вот почему тестовый код b.getvar()
, возвращаемый 3
один раз b.__class__._test_parent__var
, присваивается ему, потому что это значение, к которому обращается метод getvar .