почему подкласс обращается к атрибуту суперкласса, несмотря на наличие собственного атрибута с тем же именем, когда этот атрибут объявлен закрытым? - PullRequest
2 голосов
/ 24 марта 2019

У меня есть родительский класс Животное и дочерний класс Собака .Я хотел создать 1 экземпляр каждого и напечатать их count .Вот рабочий код:

class Animal:
  count=0
  def __init__(self):
     Animal.count+=1
  @classmethod
  def getCount(cls):
     return cls.count

class Dog (Animal):
  count=0
  def __init__(self):
    super().__init__()
    Dog.count+=1

 a1=Animal()
 print(Animal.getCount(),Dog.getCount())
 d1=Dog()
 print(Animal.getCount(),Dog.getCount())

Он печатает:
1 0
2 1

, что правильно, поскольку есть 2 животных, но только 1 из них - собака.

Проблема возникает, когда я создаю count переменную как private __ count без изменения какого-либо другого фрагмента кода.

class Animal:
  __count=0
  def __init__(self):
    Animal.__count+=1
  @classmethod
  def getCount(cls):
    return cls.__count

class Dog (Animal):
  __count=0
  def __init__(self):
    super().__init__()
    Dog.__count+=1

a1=Animal()
print(Animal.getCount(),Dog.getCount())
d1=Dog()
print(Animal.getCount(),Dog.getCount())



Теперь он печатает:
1 1
2 2

Похоже, Dog класс получает доступ только к Animal's __ count .
Можете ли вы обнаружить ошибку в коде?

Ответы [ 2 ]

4 голосов
/ 24 марта 2019

Краткий ответ

Если атрибут private , например __count, это означает, что к нему можно получить доступ только из в пределах того же класса .Доступ к Animal.__count возможен только в пределах Animal, а доступ к Dog.__count возможен только в Dog.

Поскольку getCount определен в Animal, он имеет доступ только к Animal.__count, вот что он возвращает.

Если вы хотите получить доступ к «закрытой» переменной подкласса, используйте один префикс подчеркивания, например _count.

Связанное чтение:

Подробно

Закрытые переменные реализуются с помощью механизма, называемого name mangling .Начиная с документов :

Поскольку существует действительный вариант использования для закрытых членов класса (а именно, чтобы избежать конфликтов имен имен с именами, определенными подклассами), существует ограничениеподдержка такого механизма, называемого name mangling . Любой идентификатор вида __spam (по крайней мере два начальных подчеркивания, максимум одно конечное подчеркивание) текстуально заменяется на _classname__spam, где classname - имя текущего класса с удаленным начальным подчеркиванием (ями). Это искажение выполняется без учета синтаксической позиции идентификатора, если оно происходит в пределах определения класса.

Это означает, что ваш код переведен на это:

class Animal:
    _Animal__count = 0

    def __init__(self):
        Animal._Animal__count += 1

    @classmethod
    def getCount(cls):
        return cls._Animal__count

class Dog(Animal):
    _Dog__count = 0

    def __init__(self):
        super().__init__()
        Dog._Dog__count += 1

Если вы посмотрите на это так, очевидно, что getCount не может получить доступ к переменной Dog __count.

2 голосов
/ 24 марта 2019

Это вызвано искажением имени , выполненным Python, когда любая переменная уровня класса начинается как минимум с двух подчеркиваний и имеет не более одного подчеркивания в конце.

Например:

class Foo:
    __bar = 10

Теперь к Foo.__bar можно обращаться за пределами Foo класса как Foo._Foo__bar.


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


Если вы хотите следовать коварному маршруту и ​​сохранить свою текущую структуру, вы можете определитьgetCount метод суперкласса для возврата значений, основанный на классе, из которого он вызван:

In [1720]: class Animal: 
      ...:     __count=0 
      ...:     def __init__(self): 
      ...:         Animal.__count += 1

      ...:     @classmethod 
      ...:     def getCount(cls):     
      ...:         return cls.__count if cls.__name__ == 'Animal' else getattr(cls, f'_{cls.__name__}__count') 

Кроме того, вы можете использовать snake_case для имен методов, например get_count и 4 пробела для отступа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...