【问题标题】:AttributeError not generated in case of passing method reference在传递方法引用的情况下不会生成 AttributeError
【发布时间】:2016-09-17 16:01:37
【问题描述】:

我对 python 2.7 有一个简单的疑问:

我已经创建了一个抽象基类和一个子类:

from abc import ABCMeta, abstractmethod


class Base:

    """
    Abstract base class for all entities.
    """
    __metaclass__ = ABCMeta

    def __init__(self, name):
        self.name = name

    def send_data(self):
        self.send_data()

class Child (Base):

    def __init__(self, name):
        super(Child, self).__init__(name=name)

当创建子类的对象并调用 send_method 时,我收到以下错误,这是预期的行为:

sample = Child('test')
sample.send_data()

# …
RuntimeError: maximum recursion depth exceeded

但是当 send_method 引用在基类中传递并通过创建子类对象调用 send_method 时,我认为预期的行为是接收AttributeError,但我很惊讶地发现没有生成错误。请解释一下。

from abc import ABCMeta, abstractmethod

class Base:

    """
    Abstract base class for all entities.
    """
    __metaclass__ = ABCMeta

    def __init__(self, name, parent):
        self.name = name
        self.parent = parent

    def send_data(self):
        self.send_data

sample = Child('test')
sample.send_data()

# No error

【问题讨论】:

  • 我不确定您为什么会收到属性错误。你一个属性send_dataBase.send_data 方法是一个属性。

标签: python abstract-base-class


【解决方案1】:

在您的第一个示例中,您只是创建了一个递归函数:

def send_data(self):
    self.send_data()

这无止境地调用自己,这就是为什么你最终会遇到递归深度异常。

您的第二个示例实际上并未调用该方法

def send_data(self):
    self.send_data

这里的唯一区别是您忘记使用()

这些都与抽象基类或继承无关。您没有将 send_data 函数标记为抽象函数,即使您这样做了,使用 abstractmethod 所做的只是使创建一个没有具体实现来替换它的类的实例是不可能的。

您不会仅仅因为您在 ABCMeta 类上定义了一个方法而得到 AttributeError。请注意,方法只是类的属性;他们没有单独的命名空间。 self.send_data 引用绑定的方法,而不是其他一些单独的属性。引用该方法而不调用它,否则不会执行任何操作。

【讨论】:

  • @vKohli:不,它不会,因为instance.send_data() 会找到被覆盖的实现,而不是父类上的实现。
  • @vKohli: self 只是对实例的另一个引用。普通属性查找规则适用;先看实例属性,再看MRO顺序的类。因此,如果实例上不存在send_data,则会找到Child.send_dataBase.send_data 是否存在并不重要。如果没有Child.send_data,则找到Base.send_data。在一个方法内,self 仍然是同一个实例,所以在 Base.send_data 内,引用 self.send_data 再次找到完全相同的方法。
  • 非常感谢您的解释比我问的问题要好得多。我是 python 的新手,现在我知道 child.send_data 将在 MRO 订单之前被引用。我还有一个疑问 self.send_data 指的是方法 send_data() 它没有被调用,但只是为了确保它仍然不是一个新属性?
  • @vKohli:这不是一个新属性; self.send_data 只是对方法的引用;你可以将它存储在一个变量中,然后也可以调用它。
  • 感谢您的解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-15
  • 1970-01-01
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-22
相关资源
最近更新 更多