【问题标题】:How do overridden method calls from base-class methods work?基类方法的重写方法调用如何工作?
【发布时间】:2017-05-05 07:22:57
【问题描述】:

根据docs on inheritance

派生类可以覆盖其基类的方法。由于方法在调用同一对象的其他方法时没有特殊权限,因此基类的方法调用同一基类中定义的另一个方法可能最终会调用覆盖它的派生类的方法。

这是怎么发生的?有人可以用一个简单的例子来说明这个概念吗?

【问题讨论】:

  • 来自帮助中心:“如果您的问题可以用整本书来回答,或者有很多有效的答案,那么对于我们的格式来说,它可能过于宽泛”。尝试在离您最近的 Google 信息亭中输入 python 继承。阅读前五个链接,然后针对您不理解的具体内容提出具体问题。
  • 我认为这是一个很好的问题。作者提出的问题不是一般意义上的继承,而是一个非常具体的 Python 特性。

标签: python python-3.x class inheritance


【解决方案1】:

这是您要求的示例。这将打印chocolate

class Base:
    def foo(self):
        print("foo")
    def bar(self):
        self.foo()

class Derived(Base):
    def foo(self):
        print("chocolate")

d = Derived()
d.bar()  # prints "chocolate"

字符串chocolate 被打印而不是foo,因为Derived 覆盖了foo() 函数。即使bar()Base 中定义,它最终还是调用foo()Derived 实现而不是Base 实现。

【讨论】:

  • 啊,这很简单。 100% 清楚,Baseinstance 方法永远无法执行Derived 的方法?我原以为文档是在暗示的。
  • @innisfree:你是对的,他们不能。想象一个设置,其中您有多个来自一个基类的派生类。基类将查看哪个派生类来执行该方法?
  • @innisfree d 也是Base 的一个实例,这可能是造成混淆的原因。但是是的,Base().bar() 不会调用任何被覆盖的方法
  • @innisfree 当然,除非你用猴子修补实例。例如,b = Base(); b.foo = Derived.foo.__get__(b); b.bar()。但是如果没有你明确地为它输入代码,这永远不会发生。
【解决方案2】:

它是如何工作的?

当对类的实例执行属性查找时,类字典其基类的字典按一定的顺序(参见:Method Resolution Order)搜索适当的方法。 first 找到的内容将被调用。

使用以下Spam 示例:

class Spam:
    def produce_spam(self):
        print("spam")
    def get_spam(self):
        self.produce_spam()

class SuperSpam(Spam):
    def produce_spam(self):
        print("super spam")

Spam 定义了函数produce_spamget_spam。这些存在于其Spam.__dict__(类命名空间)中。子类SuperSpam 通过继承可以访问这两种方法。 SuperSpam.produce_spam 不会替换 Spam.produce_spam,它只是在其一个实例上查找名称 'produce_spam' 时首先找到的。

从本质上讲,继承的结果是,如果在对子类进行属性查找后,在子类的字典。

当函数get_spam第一次被调用时:

s = SuperSpam()
s.get_spam()

事件的顺序大致是这样的:

  • 查看SuperSpams __dict__ 以获得get_spam
  • 由于在SuperSpams __dict__ 中找不到它,请查看它的基类字典(mro 链)。
  • Spammro 链中的下一个,因此 get_spam 可以在 Spam 的字典中找到。

现在,当produce_spamself.produce_spamget_spam 的正文中查找时,序列要短得多:

  • 查看 SuperSpam 的 (self) __dict__ 获取 produce_spam
  • 找到、获取并调用它。

produce_spam 首先在__dict__ 中找到,以便获取。

【讨论】:

    【解决方案3】:
    class Base():
        def m1(self):
            return self.m2()
        def m2(self):
            return 'base'
    
    class Sub(Base):
        def m2(self):
            return 'sub'
    
    b = Base()
    s = Sub()
    print(b.m1(), s.m1())
    

    打印“基础子”

    【讨论】:

      【解决方案4】:

      为了说明它是如何工作的,请考虑以下两个类:

      class Parent(object):
          def eat(self):
              print("I don't want to eat that {}.".format(self.takefrompocket()))
      
          def takefrompocket(self):
              return 'apple'
      
          def __getattribute__(self, name):
              print('Looking for:', name)
              method_to_use = object.__getattribute__(self, name)
              print('Found method:', method_to_use)
              return method_to_use
      
      class Child(Parent):
          def takefrompocket(self):
              return 'salad'
      

      __getattribute__ 方法在新样式类(如 python3 中的所有类)中负责属性查找。它只是针对print 实现每个查找所做的 - 通常您不想也不应该自己实现它。如果您真的感兴趣,则查找遵循 pythons method resolution order (MRO)

      >>> some_kid = Child()
      >>> some_kid.eat()
      Looking for: eat
      Found method: <bound method Parent.eat of <__main__.Child object at 0x0000027BCA4EEA58>>
      Looking for: takefrompocket
      Found method: <bound method Child.takefrompocket of <__main__.Child object at 0x0000027BCA4EEA58>>
      I don't want to eat that salad.
      

      因此,当您想使用 eat 时,它在此示例中使用 Parent.eat。但是self.takefrompocket 是从Child 使用的。

      >>> some_parent = Parent()
      >>> some_parent.eat()
      Looking for: eat
      Found method: <bound method Parent.eat of <__main__.Parent object at 0x0000027BCA4EE358>>
      Looking for: takefrompocket
      Found method: <bound method Parent.takefrompocket of <__main__.Parent object at 0x0000027BCA4EE358>>
      I don't want to eat that apple.
      

      这两种方法都取自Parent。继承的类(通常)不会干扰它们的祖先!

      【讨论】:

        【解决方案5】:

        如果您的子类没有实现该方法,则引发异常!

        class Base(object):
        
            def something (self):
                raise ('Not implemented')
        

        【讨论】:

        • 您能否格式化您的代码并包含 OP 想要的最小示例(即带有子类和执行)?
        • 这绝不会回答“它怎么会发生”的问题。
        猜你喜欢
        • 2012-12-12
        • 1970-01-01
        • 1970-01-01
        • 2011-06-17
        • 1970-01-01
        • 2011-01-18
        • 1970-01-01
        • 2023-03-12
        相关资源
        最近更新 更多