【问题标题】:Should I be using abstract methods in this Python scenario?我应该在这个 Python 场景中使用抽象方法吗?
【发布时间】:2011-06-09 22:12:47
【问题描述】:

我不确定我的方法是不是好的设计,我希望我能得到一个小费。我正在考虑抽象方法的某个地方,但在这种情况下,我希望该方法是可选的。我现在就是这样做的……

from pymel.core import *

class A(object):
    def __init__(self, *args, **kwargs):
        if callable(self.createDrivers):
            self._drivers = self.createDrivers(*args, **kwargs)
            select(self._drivers)

class B(A):
    def createDrivers(self, *args, **kwargs):
        c1 = circle(sweep=270)[0]
        c2 = circle(sweep=180)[0]
        return c1, c2

b = B()

在上面的示例中,我只是在 PyMEL 中为 Maya 创建了 2 个圆弧,但我完全打算创建更多可能有也可能没有 createDrivers 方法的子类!所以我希望它是可选的,我想知道我的方法是否——好吧,我的方法是否可以改进?

【问题讨论】:

  • 抱歉,没有足够的信息来评论设计。从这个例子中我看不出有任何理由使用 A 以及为什么 B 是 A。现在的形式我认为你想太多了 ;)
  • PEP 8 -- Python 代码风格指南:python.org/dev/peps/pep-0008
  • 感谢@Pawel Prażak,但我只是想将我的代码压缩成人们实际阅读的内容。我这样做肯定是有原因的。我的A 类实际上是RigControl,子类就像CircleControlCrossControl 等。我要制作许多形状,我想继承RigControl 的所有功能。
  • @delnan,继续前进,我一定会优先考虑这一点。感谢您的提示。

标签: python subclass optional abstract-methods pymel


【解决方案1】:

你仍然有一个问题,当你继承你的B类时,这将调用A.__init__,如果你没有在子类中实现createDrivers,这行callable(self.createDrivers)将抛出一个错误,因为@987654324 @ 不存在(AttributeError)我想如果我是你我会这样做:

class A(object):
    def __init__(self, *args, **kwargs):
       try:
           self._drivers = self.createDrivers(*args, **kwargs)
           select(self._drivers)
       except NotImplementedError:
           pass

    def createDrivers(self, *args, **kwargs):
        raise NotImplementedError("This class wasn't implemented")

class B(A):
    def createDrivers(self, *args, **kwargs):
        c1 = circle(sweep=270)[0]
        c2 = circle(sweep=180)[0]
        return c1, c2

class C(A):
    pass

另一种方法是将callable(self.createDrivers) 替换为hasattr(self, 'createDrivers')

【讨论】:

  • 我可以看到这个话题会引起很大的争议。每个人都在给出有效的例子,但没有一个给我温暖的模糊。我无法决定哪种方法最好。他们都有自己的优点和缺点。这个缺点是它外观是一个抽象方法,而实际上它的意图是可选的。
  • @sfjedi:这种方法有一个优点是失败的大声礼节,而其他方法没有,我认为这在实现 API 或其他方法时非常重要,因为假设有人会创建一个的 C c = C() 就像在我的示例中一样,当它调用 c.createDrivers 这将失败,因为 createDrivers 是 not Implemented 这不像 AttributeError 因为这个错误告诉使用什么是错误的,另一方面,如果有人打电话给c.createDrivers,其他方法不会这样做!只是一个无声的错误该方法只是存在并且什么都不做!!??
  • @Lennart Regebro:OP 的问题是 我应该在这个 Python 场景中使用抽象方法吗?,对于可选的方法,实际上我不明白你的意思意思是,从您的回答中,我看不出我们两种方法之间有什么区别,只是我的方法有能力大声失败,这很有意义,而在您的方法中,您只是什么都不做,例如告诉某人他想要从一个没有实现它的类调用createDrivers:“好吧,你调用了该方法,但没有发生任何事情,请阅读代码源或文档字符串以了解原因!!!”
  • @singularity:你的论点很好。事实上,我觉得这样做真的很糟糕,因为我已经接受了@Lennart Regebro 的解决方案,但我倾向于接受你的论点。我在这里寻找的是一个可能会或可能不会实现的虚函数,并且您的解决方案在基类和任何子类中都是可读的。我想这就是我想要的。
  • @Lennart Regebro:我仍然看不到我的示例如何使用“抽象方法”,因为当我创建一个时,抽象方法应该大声失败 b>从基类继承并且没有实现我的“抽象方法”,当我调用实例的方法(这就是我所做的)并且如果我以正确的方式使用抽象方法时不会失败它是使用abc.abstractmethod 装饰器,并且从基类继承的每个类必须实现该方法,我同意这是在python中声明抽象方法的旧方法,但现在不再我们有 ABC 模块 :)
【解决方案2】:

我会这样做:

class A(object):
    def __init__(self, *args, **kwargs):
        self.createDrivers(*args, **kwargs)

    def createDrivers(self, *args, **kwargs):
        "Override"
        pass

class B(A):
    def createDrivers(self, *args, **kwargs):
        self._drivers = blabla

【讨论】:

  • 我真的很喜欢你的解决方案的可读性,但公平地对待上面的@Lennart Regebro,他的解决方案几乎是一样的,他首先发布了,所以我担心解决方案会他;不过,我会投票给你。
  • 我并不是要为一些 stackoverflow 点争吵,但我想我在 Lennart 前一分钟发布了;)。尽管如此,还是感谢您的支持。
  • 没什么大不了的。事后看来,Lennart 的答案更加明确,将 createDrivers 的输出分配给 init 方法中的 self._drivers。因此,我可能会选择他的答案。
  • @möter:顺便说一句,你在第一个 **kwargs 上错过了一个星号。
【解决方案3】:

如果您希望 createDrivers 是可选的,但仍然始终存在,最好的方法不是抽象方法,而是在基类中将其实现为 noop。

class A(object):
    def __init__(self, *args, **kwargs):
        self._drivers = self.createDrivers(*args, **kwargs)
        select(self._drivers)

    def createDrivers(self, *args, **kwargs):
        """This should be overridden by subclasses if they need custom drivers"""
        pass

【讨论】:

  • 但是if callable 行总是会评估为真,不是吗?
  • 是的,但是您可以删除它,如果并在调用 self.createDrivers 之后添加 if self._drivers: 或类似名称。
  • 我知道你打算用这个做什么,这是一个很好的工作建议。告诉你什么——像你说的那样更新你的帖子,并像下面的@möter那样添加"Override"评论,我会接受你的解决方案。
  • @sfjedi:对,我忘了删除它。现在不见了。
猜你喜欢
  • 2015-05-06
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 2016-08-13
  • 1970-01-01
  • 1970-01-01
  • 2012-07-23
  • 1970-01-01
相关资源
最近更新 更多