要求特定返回类型

2024-06-06 07:32:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我遇到了一种情况,需要从另一个实现者那里得到要求,请遵循我的基类设计:

class ExemplaryClass:
    pass

class Base(ABC):
    @abstractmethod
    def process(self):
        # should return specific type, for instance ExemplaryClass

以及将由其他程序员创建的派生类:

class Derived(Base):
    def process(self):
        # if return sth different from ExemplaryClass, should throw exception

我怎样才能用代码强迫程序员遵循我的设计呢? 在C++和其他带有静态类型控制的语言中,这个问题很简单:

class ExemplaryClass {}

class Base{
public:
    virtual ExemplaryClass proccess() =0;
}

class Derived :public Base {
public:
    // ERROR
    void proccess() ovveride { }
}

但是在像python这样带有动态类型控制的语言中,这种情况非常复杂,我不知道如何解决这个问题。你知道吗


Tags: self语言类型basereturndef情况public
2条回答

正如其他人在评论中指出的那样,做这种类型检查是不好的做法。但是如果您真的想这样做,可以将process分成两个方法—一个抽象方法和另一个调用抽象方法并验证其返回值的方法:

class Base(ABC):
    def process(self):
        result = self._process()
        assert isinstance(result, ExemplaryClass)
        return result

    @abstractmethod
    def _process(self):
        raise NotImplementedError

我终于想出了解决办法。你知道吗

其思想是在主类中实现__getattribute__,因此必须修改子类调用的方法。你知道吗

考虑两个简单的类:由模块定义的Foo基类和由API用户定义的Bar子类。 Bar类覆盖method方法,但您希望它返回类型为int的对象,以便保持与Foo.method相同的行为。你知道吗

class Foo:
    def method(self):
        return 1

class Bar(Foo):
    def method(self):
        return "a"

当然,这没关系。 但是这里有一个使用__getattribute__方法的技巧。你知道吗

我首先检查请求的属性是否命名为"method"。 如果是的话,我就创建一个自定义函数,它不是返回实际的method,而是断言实际的method的返回值是一个int。你知道吗

def __getattribute__(self, attribute):
    if attribute == "method":

        def _f(*args, **kwargs):
           result = type(self).method(self, *args, **kwargs)
           assert isinstance(result, int)
           return result

        return _f

现在,__getattribute__方法的定义是这样的:instance.whatever将导致对instance.__getattribute__("whatever")的调用,而不管whatever是否可以作为继承族中任何类的属性找到。你知道吗

因此,如果bar = Bar(),那么bar.method将被我在Foo.__getattribute__中定义的内部_f替换。你知道吗

现在,bar.method()将产生一个AssertionError,但是如果我按如下方式修改它:

class Bar(Foo):
    def method(self):
        return 1

那么死刑就可以了。你知道吗


关于__getattribute__的定义,可能有点棘手,因为您不能调用self.something,因为它会导致对__getattribute__的递归调用。 解决方案是直接引用type(self).method,它引用了这里由Bar定义的类方法。 在这种情况下,此方法将instance作为第一个参数,因此必须传递self,因此行:

result = type(self).method(self, *args, **kwargs)

当然,如果子类本身实现__getattribute__,所有这些都将失败。 但无论如何,不要忘记pythonapi很容易被破坏。 更具体地说,AssertError很容易被捕获。 好吧,你可以用sys.exit(1)代替assert,但这可能有点太强制性了。。。你知道吗


我有个主意。 这或多或少是一种反模式,Python方面的,因为它禁止用户自由地做他们想做的事情。 另一方面,人们可以用一个简单的print来代替assert/raise,以便教育用户,而不是惩罚他们。你知道吗

相关问题 更多 >