【问题标题】:"Hidden" attributes or duplicated code in python when using inheritance使用继承时python中的“隐藏”属性或重复代码
【发布时间】:2020-07-24 02:50:59
【问题描述】:

我对在 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 保留在与Cat 不同的不同文件 中。对于阅读CatDog 类的代码的人,使用number_of_legs 属性但未在文件中定义。我的理解是最好不要有定义不透明的变量(这就是为什么最好避免from x import *

我认为替代方案是在两个子类中重复定义 self.number_of_legs,但这违背了继承的目的。

是否有处理这种情况的最佳实践?

【问题讨论】:

  • 一个评论,我建议。
  • 我不能 100% 确定是否每个人都会同意我的观点,但从工作经验中我了解到,我的很多同事实际上更喜欢从某些文件中抽象出变量(这意味着它们是在别处定义)。在我看来,我认为这种风格没有任何问题,并且实际上更喜欢它。大多数 IDE 还将有一个“转到定义”选项,其他人可以看到该变量的定义位置。再说一次,只是我个人的看法。
  • 定义并不是真正的“不透明”。 number_of_legsself 限定,所以如果它没有在子类中定义,那么它必须在超类中定义。重复定义充其量是没有帮助的,并且可能会导致错误。

标签: python oop inheritance module object-oriented-analysis


【解决方案1】:

有处理这种情况的最佳实践吗?

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

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”可以很好地传达腿的数量。

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-22
      • 2019-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      相关资源
      最近更新 更多