为了账号安全,请及时绑定邮箱和手机立即绑定

使用继承时python中的“隐藏”属性或重复代码

使用继承时python中的“隐藏”属性或重复代码

幕布斯7119047 2023-03-30 17:27:40
我有一个问题,关于在 python 中使用继承时我认为什么是潜在的坏习惯假设我有一个基类class FourLeggedAnimal():    def __init__(self, name):        self.name = name        self.number_of_legs = 4和两个女儿班class Cat(FourLeggedAnimal):    def __init__(self, name):        super().__init__(name)    def claw_the_furniture(self):        for leg in range(self.number_of_legs):        print("scratch")class Dog(FourLeggedAnimal):    def __init__(self, name):        super().__init__(name)         def run_in_sleep(self):        for leg in range(self.number_of_legs):        self.move_leg(leg)    def move_leg(i):        pass出于本示例的目的,我打算Animal将. 对于阅读或类代码的人来说,该属性已使用但未在文件中定义。我的理解是最好不要有定义不透明的变量(这就是为什么最好避免使用.CatCatDognumber_of_legsfrom x import *我看到了在两个子类中重复定义的替代方案self.number_of_legs,但这违背了继承的目的。是否有处理这种情况的最佳做法?
查看完整描述

2 回答

?
千万里不及你

TA贡献1784条经验 获得超9个赞

是否有处理这种情况的最佳做法?


通常,类变量用于此目的。


class FourLeggedAnimal():

    number_of_legs = 4                    # class variable


    def __init__(self, name):

        self.name = name


class Cat(FourLeggedAnimal):

    def __init__(self, name):

        super().__init__(name)


    def claw_the_furniture(self):

        for leg in range(self.number_of_legs):

            print("scratch")


class Dog(FourLeggedAnimal):

    def __init__(self, name):

        super().__init__(name)

     

    def run_in_sleep(self):

        for leg in range(self.number_of_legs):

            self.move_leg(leg)


    def move_leg(i):

        pass

请注意,即使这些类位于不同的文件中,该属性也是父类公共 API 的一部分并且子类可以知道。此外,类名“FourLeggedAnimal”很好地传达了腿的数量。


查看完整回答
反对 回复 2023-03-30
?
肥皂起泡泡

TA贡献1829条经验 获得超6个赞

我的理解是最好不要有定义不透明的变量(这就是为什么最好避免 from x import *.

我认为您可能误解了此建议的来源。它甚至可能是不同建议的混合体。我将尝试解释我认为可能是人们试图传达的潜在想法。

首先,人们普遍认为from x import *最好避免在 Python 中使用。这是因为它使读者很难找出名称的来源,或者是否确实定义了名称。它还混淆了一些代码分析工具。这是(非内置)名称通常进入顶级命名空间而不会出现在源代码中并且易于搜索的唯一方法。就此建议而言,它适用于这种情况。如果您不能在对象上使用字段和方法,那么您根本无法编写 Python 代码,而且您通常有一个清晰的面包屑痕迹可以遵循。(此外,如果您使用类型注释。)

但是,您可能还会想到封装的原理。在面向对象的编程中,最好将接口与对象的实现分开。您可以使界面尽可能小、简单和清晰,并使用代码隐藏实现对象。通过这种方式,您可以独立地推理和更改实现,并确信这样做不会影响其他代码。这个原则甚至适用于基类和子类——子类不应该“知道”它不需要的关于基类的任何事情。现在,修改变量,以及在较小程度上阅读可修改的变量,需要非常了解基类对其值的期望、它们与其他状态的关系以及它们何时可能/允许更改。依赖它们会使安全更改基类变得更加困难。

现在,Python 在这方面确实比其他一些语言具有更大的灵活性。在 Python 中,您可以无缝地将变量替换为属性,从而将“读取”和“设置”字段转换为您可以根据需要实现的方法。在其他语言中,一旦子类开始使用基类公开的字段,就不可能重构基类以删除该字段或在访问它时添加任何额外行为,除非您还更新所有子类。所以这有点不太重要。或者更确切地说,没有特别的理由将字段与方法区别对待。

考虑到所有这些,问题就变成了——您的基类向其子类呈现的接口是什么?它是否支持他们设置以及读取该字段?您能否在不使代码更复杂的情况下减小两个类之间接口的大小和复杂性?如果一个接口是只读的,那么它更简单,更容易推理,如果它根本不涉及可变状态则更是如此。在可能的情况下,基类不应给子类任何不必要的机会来破坏其不变量。(即它是对其自身状态的期望。)在 Python 中,这些事情通常是通过约定(例如,以下划线开头的字段和方法被认为不是公共接口的一部分,除非另有说明)和文档而不是通过语言特性来实现。


查看完整回答
反对 回复 2023-03-30
  • 2 回答
  • 0 关注
  • 141 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信