【问题标题】:why is subclass accessing superclass' attribute inspite of having its own attribute of same name when that attribute is declared private?为什么子类访问超类的属性,尽管当该属性被声明为私有时,它有自己的同名属性?
【发布时间】:2019-03-24 14:01:37
【问题描述】:

我有一个父类 Animal 和子类 Dog。我想为每个创建 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 变量创建为私有 __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
你能检测出代码中的错误吗?

【问题讨论】:

标签: python oop


【解决方案1】:

简短的回答

当一个属性是私有时,比如__count,这意味着它只能从在同一个类中访问。 Animal.__count只能在Animal内访问,Dog.__count只能在Dog内访问。

因为getCount是在Animal中定义的,所以它只能访问Animal.__count,所以这就是它返回的内容。

如果您想访问子类的“私有”变量,请使用单个下划线前缀,例如 _count

相关阅读:

详细介绍

私有变量是通过一种称为name mangling的机制实现的。来自the docs

由于类私有成员有一个有效的用例(即 避免名称与子类定义的名称发生名称冲突),有 对这种机制的支持有限,称为 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变量。

【讨论】:

  • 只能在同一个类中访问——这是不对的。请看我的回答。
  • getCount 在 Animal 中定义,但也在 Dog 中继承。 Dog 中定义了另一个 __count。那么 Dog.getCount() 不应该返回 Dog 的 __count 吗?
  • @irootaku 继承一个方法不会复制它。它在Animal 中定义,因此无法访问私有Dog 属性。有关更多详细信息,请参阅the docs
【解决方案2】:

这是由 Python 完成的name mangling 在任何类级别变量以至少两个下划线开头,并且在末尾最多有一个下划线时造成的。

例如:

class Foo:
    __bar = 10

现在,Foo.__bar 可以在 Foo 类之外作为 Foo._Foo__bar 访问。


在您的情况下,您最好只使用一个下划线,即_count 作为变量名,这表明该名称仅供私人使用。


如果你想遵循abtitious路线并保持你当前的结构,你可以定义超类的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 个空格用于缩进。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多