7

我有一些 Python 类,如果简化的话,看起来像:

class base:
    def __init__(self, v):
        self.value = v

    def doThings(self):
        print "Doing things."

    def doMoreThings(self):
        print "Doing more things."

    def combine(self, b):
        self.value += b.value

class foo(base):
    def showValue(self):
        print "foo value is %d." % self.value

class bar(base):
    def showValue(self):
        print "bar value is %d." % self.value

该类base包含方法(上面由doThings和表示doMoreThings),这些方法实现了foobar子类共有的功能。foobar子类在解释value字段的方式上有本质的不同。(上面,它们仅在打印时显示的内容有所不同,但在我的实际应用程序中,它们会做其他一些更复杂的事情。) base可以被认为是“抽象的”:用户只使用foos 和bars。 base仅作为其子类共有的代码的家而存在。

我想问的方法是combine,它可以让你取其中两个对象并制作第三个。因为foobar解释value不同,对不同类型的两个子类没有意义combine:您可以使用combine两个foos 来获取 afoo或两个bars 来获取 abar但不能 afoo和 a bar。即便如此,所有子类的过程combine都是相同的,因此将其分解并在一个地方定义是有意义的。

如果用户尝试使用两个不兼容的对象,我可能想发出错误信号combine,但我看不到在不引入丑陋类型检查的情况下执行此操作的方法。这样做是个好习惯吗?或者我应该做通常的事情而不是检查,记录问题,并假设用户不会尝试以combine非预期的方式使用,即使这种使用似乎“成功”并返回垃圾对象而不是引发错误?

感谢您的帮助。

4

4 回答 4

3

我在这里看到了几种方法:

  1. 不要检查任何东西并相信用户会做正确的事情。可能合适或危险,视情况而定。

  2. 检查类型。你说得对,它看起来很丑,但这是最简单的事情。

  3. 没有 a value,但按照它们的预期方式命名它们 ( pressure, temperature) 并让它们组合起来。

  4. 与 3 相同,但另外有子类有一个property将访问映射.value到它们各自的“真实”值变量。这样,您可以保留 . __init__(),但.combine()必须由每个子类完成。

  5. 与 4 相同,但不使用property,而是自制描述符。在这个答案中,我展示了如何做到这一点。

于 2013-05-10T06:57:14.820 回答
2

改变组合方法

def combine(self, b):
    if (not(self.__class__.__name__ == b.__class__.__name__)):
        raise TypeError("%s cannot combine with %s" % ( self.__class__.__name__, b.__class__.__name__))
    else:        
        self.value += b.value

或者,将 base 的类定义更改为

class base(object):

然后可以使用更简单、更好、更快乐的组合方法(感谢 glglgl)

def combine(self, b):
    if (not(type(self) == type(b))):
        raise TypeError("%s cannot combine with %s" %
                        ( type(self), type(b) ))
    else:        
        self.value += b.value
于 2013-05-10T07:11:02.403 回答
0

当我编写不会失败的生产代码时,我使用了一个装饰器来确保一个有风险的函数不会爆炸、变成一个火球并炸毁我的整个系统。

def bulltproof(func):
    def _bulletproof(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
        except Exception as exc:
            raise BulletproofError(exc)
        return result
    return _bulletproof

像这样使用它:

@bulletproof
def some_risky_function():
    #....

并且只捕获一个错误:

try:
    some_risky_function()
except Bulletproof:
...

您可以返回None,记录错误或将其关闭以进行测试,而不是引发错误。当然,当你想确保你的程序从错误中恢复时,这种方法很有用,但有时最好死掉而不是继续出错

于 2013-05-10T07:15:24.290 回答
0

由于.combine()在概念上根据对象是foo或来做完全不同的事情bar,我认为不在抽象父类中拥有该函数,而是将其保留在派生类中是有意义的。即使是相同的代码,组合 的两个实例与组合 的两个实例也是foo不同的操作bar。如果 for 的代码.combine()实际上很长,您可以在基类中创建一个私有方法来完成这项工作,但只能从派生类中定义的函数中调用它。

于 2013-05-10T09:16:02.067 回答