【问题标题】:Access child class variable in parent class访问父类中的子类变量
【发布时间】:2018-03-22 18:53:52
【问题描述】:

从父类中的子类访问变量是否正确?这是一个好的 OOP 方法吗?我不需要创建 Animal 类的实例,但如果我愿意,make_sound 方法会引发 AttributeError,这让我很困扰。

class Animal:
    def make_sound(self):
        print(self.sound)

class Cat(Animal):
    sound = 'meow'

class Dog(Animal):
    sound = 'bark'

cat = Cat()
cat.make_sound()

dog = Dog()
dog.make_sound()

【问题讨论】:

  • 这正是你应该做的。如果可能的 AttributeError 困扰您,请在 Animal 中将声音设置为默认值,即使它是 None。

标签: python class oop


【解决方案1】:

这种方法本质上没有任何问题。这实际上取决于这个类的范围和意义,以及它的使用位置。构建一个父类来使用隐式定义的属性很快,而且在许多情况下完全没问题。但是,有时这些隐式属性可能会失控,您可能希望确保创建新子类的任何人必须定义这些属性。

有几种方法可以解决这个问题。根据您使用的 Python 版本,其中一些可能不起作用。我相信像这样使用 ABC 在 Python 3.4+ 中有效。

Python(和许多 OO 语言)具有Abstract Base Class 的概念。这是一个永远无法实例化的类,它强制任何子类必须实现定义为抽象的方法或属性才能被实例化。

您可以通过以下方式提供 make_sound 方法,并且仍然 100% 确定任何子类化 Animal 的人确实会发出这种声音。

from abc import ABC, abstractmethod


class Animal(ABC):

    def make_sound(self):
        print(self.sound)

    @property
    @abstractmethod
    def sound(self):
        """ return the sound the animal makes """


class Dog(Animal):

    @property
    def sound(self):
        return "bark"


class Cat(Animal):

    sound = "meow"


class Thing(Animal):
    """ Not an animal """

dog = Dog()
dog.make_sound()
cat = Cat()
cat.make_sound()
# thing = Thing()   this will raise a TypeError, complaining that its abstract
# animal = Animal()   as will this

这显示了许多不同的方法来做到这一点。使用@property 装饰器允许您设置影响它的实例变量或更复杂的逻辑。在类中设置声音(在某种程度上)类似于在 Java 类中设置静态成员。由于所有的猫都会喵喵叫,在这种情况下这可能是有道理的。

【讨论】:

【解决方案2】:
class Animal:
    def make_sound(self):
        print(self.sound)
    def make_class_sound(self):
        print(self.class_sound)

class Cat(Animal):
    class_sound = "Meow"
    def __init__(self):
        self.sound = 'meow'

class Dog(Animal):
    class_sound = "Bark"
    def __init__(self):
        self.sound = 'bark'

cat = Cat()
cat.make_sound()
cat.make_class_sound()

dog = Dog()
dog.make_sound()
dog.make_class_sound()
$ python tt.py
meow
Meow
bark
Bark

我想这段代码 sn-p 不仅可以帮助您了解如何访问父类中的子变量,还可以区分实例变量和类变量。 “self.class_sound”的类变量在自己的实例变量中搜索后,最终会在子类中被引用。

【讨论】:

    【解决方案3】:

    是的,这是很好的 OOP。您可以从父类继承,并简单地在父类中创建您将在 Cat 或 Dog 类的实例中调用的方法,这样您就可以不向 Cat/Dog 类添加任何内容:例如:

    class Animal():
        def make_sound(self):
            print(self.sound)
    
        def make_dog_sound(self):
            print('bark')
    
        def make_cat_sound(self):
            print('meow')
    
    class Cat(Animal):
          pass
    
    class Dog(Animal):
        pass
    
    cat = Cat()
    cat.make_cat_sound()
    

    但你所做的也是正确的。

    【讨论】:

    • 我明白你的意思,但在你的例子中,猫会叫,这有点奇怪
    • 如果我有多种类似的方法(如“声音”),我有时会这样做。哈哈,猫停车会很奇怪。但是为了减少混淆,如果方法相似,我会在父类中添加很多方法,以减少对我个人的混淆。你这样做的方式也很好。
    • 这是糟糕的设计。基类不知道动物会从它继承什么。
    猜你喜欢
    • 2011-04-08
    • 1970-01-01
    • 2018-08-15
    • 2023-04-01
    • 2020-12-07
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多