【问题标题】:Is it correct to inherit from built-in classes?从内置类继承是否正确?
【发布时间】:2010-09-22 06:25:52
【问题描述】:

我想以某种方式用 python 程序解析一个 Apache access.log 文件,虽然我对面向对象编程完全陌生,但我想现在就开始做。

我要创建一个类ApacheAccessLog,而我现在唯一能想象到的,它会做的是'readline' 方法。在这种情况下从内置的 file 类继承传统上是否正确,因此该类的行为就像 file 类本身的实例一样,或者不是?最好的方法是什么?

【问题讨论】:

    标签: python inheritance oop


    【解决方案1】:

    在这种情况下,我会使用委托而不是继承。这意味着您的类应该包含文件对象作为属性并在其上调用readline 方法。您可以在记录器类的构造函数中传递一个文件对象。

    这至少有两个原因:

    1. 委托减少了耦合,例如,您可以使用任何其他实现readline 方法的对象代替文件对象(duck typing 在这里很方便)。
    2. 从文件继承时,类的公共接口变得不必要的宽泛。它包括文件中定义的所有方法,即使这些方法在 Apache 日志的情况下没有意义。

    【讨论】:

      【解决方案2】:

      我来自 Java 背景,但我相当有信心相同的原则将适用于 Python。根据经验,您应该永远从您不理解和控制其实现的类继承,除非该类是专门为继承而设计的。如果它是以这种方式设计的,它应该在其文档中清楚地描述这一点。

      这样做的原因是继承可能会将您绑定到您要继承的类的实现细节。

      使用 Josh Bloch 的《Effective Java》一书中的示例

      如果我们要扩展类 ArrayList 类以便能够计算在其生命周期内添加到它的项目的数量(不一定是它当前包含的数量),我们可能会想写像这样的。

      public class CountingList extends ArrayList {
          int counter = 0;
      
          public void add(Object o) {
              counter++;
              super.add(0);
          }
      
          public void addAll(Collection c) {
              count += c.size();
              super.addAll(c);
          }
      
          // Etc.
      }
      

      现在这个扩展看起来可以准确地计算添加到列表中的元素数量,但实际上它可能不会。如果ArrayList 通过迭代提供的Collection 并为每个元素调用其接口方法addAll 实现了addAll,那么我们将对通过addAll 方法添加的每个元素计数两次。现在我们类的行为依赖于ArrayList的实现细节。

      这当然是除了不能将List 的其他实现与我们的CountingList 类一起使用的缺点之外。加上上面讨论的从具体类继承的缺点。

      据我了解,Python 使用与 Java 相似(如果不相同)的方法分派机制,因此会受到相同的限制。如果有人可以提供 Python 中的示例,我相信它会更加有用。

      【讨论】:

        【解决方案3】:

        从内置类继承是完全可以接受的。在这种情况下,我会说你是对的。
        日志“是一个”文件,所以告诉你继承没问题..

        一般规则。
        狗“是”动物,因此继承自动物。
        所有者“拥有”动物,因此不从动物继承。

        【讨论】:

          【解决方案4】:

          虽然在某些情况下从内置函数继承很有用,但这里真正的问题是您想对输出做什么以及您的总体设计是什么。我通常会编写一个阅读器(使用文件对象)并吐出我需要保存我刚刚阅读的信息的任何数据类。然后很容易设计该数据类以适应我的其余设计。

          【讨论】:

            【解决方案5】:

            从“内置”类继承应该是相当安全的,因为以后对这些类的修改通常会与当前版本兼容。

            但是,您应该认真考虑您是否真的希望将您的类与内置类提供的附加功能联系起来。正如另一个答案中提到的,您应该考虑(甚至可能更喜欢)使用 delegation 代替。

            作为一个示例,如果您不需要继承,为什么要避免继承,您可以查看 java.util.Stack 类。当它扩展 Vector 时,它继承了 Vector 上的所有方法。这些方法中的大多数都违反了 Stack 隐含的合同,例如后进先出。在内部使用 Vector 实现 Stack 会好得多,只需将 Stack 方法公开为 API。之后很容易将实现更改为 ArrayList 或其他内容,但由于继承,现在这些都无法实现。

            【讨论】:

              【解决方案6】:

              您似乎已经找到了答案,即在这种情况下,委托是更好的策略。尽管如此,我想补充一点,除了委派之外,扩展内置类并没有错,特别是如果您的替代方案(取决于语言)是“猴子补丁”(请参阅​​http://en.wikipedia.org/wiki/Monkey_patch

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-11-22
                • 1970-01-01
                • 2012-03-02
                • 2021-06-16
                • 2015-11-14
                相关资源
                最近更新 更多