实现在类中调用方法的序列

时间:2017-09-19 06:21:02

标签: python

让我们说我们想要使用类

实现一个功能
class MyClass():
    def __init__(self):
        pass

    def generate_query(self):
        # Generate the query
        pass

    def send_query(self):
        # send the query over the network
        pass

    def receive_response(self):
        # read the response from buffer
        pass

确保在generate_query()之前调用send_query()的最佳方式是什么,反之亦然,这是没有意义的。这很重要,因为只需在API文档中提及在您调用send_query()之前调用另一种方法是另一种方法,但在send_query() generate_query()中的代码中明确地检查它{{1}以前被称为IMO的好习惯。

我期待一个解决方案,如果没有调用generate_query(),我们会引发异常。

有一种很好的pythonic方法可以做到这一点,我曾在某处读过,但我忘记了源码和解决方案。

非常感谢帮助!

3 个答案:

答案 0 :(得分:0)

我的想法是保持实例变量名称标志为True,如果调用generate_query,并且如果flag为false,则表示不调用generate_query,因此,您将在send_query或show中调用generate_query一条消息。

答案 1 :(得分:0)

一个选项可能是使用布尔标志。这种方法很简单,但不是很干净。

class MyClass():
    def __init__(self):
        self.generated = False

    def generate_query(self):
        self.generated = True

        # Generate query

    def send_query(self):
        if not self.generated:
            self.generate_query()

        # Send query

    def receive_response(self):
        pass

我建议你尝试重建课程和方法。要求以特定顺序运行的方法容易出错,尤其是在没有记录的情况下。

答案 2 :(得分:0)

我想出了一个可以从其继承的基类来强制执行方法调用顺序(已在Python 2.7和3.6中进行了测试):

from types import FunctionType, MethodType

class Sequenced(object):

    def __init__(self):
        _methods = [name for name, obj in self.__class__.__dict__.items()
            if type(obj) == FunctionType]
        assert set(self._method_sequence).issubset(set(_methods))
        self._sequence_pos = 0

    def __getattribute__(self, name):
        attr = object.__getattribute__(self, name)
        if type(attr) == MethodType:
            if attr.__name__ in self._method_sequence:
                if self._sequence_pos >= len(self._method_sequence):
                    raise RuntimeError("All sequenced methods already called.")
                if attr.__name__ != self._method_sequence[self._sequence_pos]:
                    raise RuntimeError("{0} method call expected.".format(
                        self._method_sequence[self._sequence_pos]))
                self._sequence_pos += 1
            def func_wrapper(*args, **kwargs):
                return attr(*args, **kwargs)
            return func_wrapper
        else:
            return attr

请注意,我不完全了解它是如何工作的(我设法阻止__getattribute__引起无限递归,但是我一开始不了解是什么原因引起的,所以我不知道无法理解为什么我必须在一个地方使用FunctionType而在另一个地方使用MethodType)。它通过了我的最低测试(Python 2.7和3.6),但是您需要确保也对其进行测试。

您需要对类进行的操作是使其继承自Sequenced,并像下面这样修改其__init__方法:

class MyClass(Sequenced):

    def __init__(self):
        self._method_sequence = [
            "generate_query",
            "send_query",
            "receive_response"
        ]
        super(MyClass, self).__init__()

    def generate_query(self):
        # Generate the query
        pass

    def send_query(self):
        # send the query over the network
        pass

    def receive_response(self):
        # read the response from buffer
        pass

基类允许您的类包含其他方法(仅对_method_sequence列表中的方法强制执行排序)。如果您希望多次调用方法,_method_sequence可以包含重复项。

如果顺序方法被不按顺序调用,或者在已经遍历列表之后尝试使用顺序方法,则会引发RuntimeError

可以修改基类,以便一旦可用就可以重复调用序列方法,但我将其留给读者练习。