为了账号安全,请及时绑定邮箱和手机立即绑定

python中的多级依赖注入

python中的多级依赖注入

暮色呼如 2022-09-13 19:58:11
我有5个类,如下所示。执行从类 5 开始。在这里,依赖注入被用作侦听器对象传递给类的构造函数,然后该对象调用 listen() 方法。这部分很好。Bot麻烦从现在开始。从方法中,我调用另一个调用的方法,该方法位于类( 文件中)中。然后,该方法从 2 个不同的类和 中调用另外两个方法。listen()process()Processorprocessor.pyprocess()TokenizerCore如您所见,所有这些都是耦合的,此处不使用依赖关系注入。我不知道该怎么做。我已经尝试了python包和包的示例,但据我所知,这些示例与单级依赖关系有关,而不是这样的情况。dependency-injectorpinject我曾想过在一个模块中创建所有必需的对象,并在需要时将其作为参数传递给所有类。但这似乎不是一个好习惯。我能做些什么来解耦上面的代码?我应该如何进行模块化?# core.pyclass Core:    def decide(self, data):        """ Makes decisions based on input data """        # blah blah-# tokenizer.pyimport nltkfrom nltk.corpus import stopwordsfrom nltk.tokenize import word_tokenizestoplist = set(stopwords.words('english'))class Tokenizer:    def tokenize(self, query):        """ Tokenize the input query string """        tokenized_query = word_tokenize(query)        clean_query = [w for w in tokenized_query if not w in stoplist]        return clean_query-# processor.pyimport tokenizerimport coreclass Processor:    def process(self, query):        """         Send the input query to tokenizer         tokenized input is send to the Core module for decision making        """        tokenized_input = tokenizer.Tokenizer().tokenize(query)        core.Core().decide(tokenized_input)-# listener.pyimport processorclass Listener:    def listen(self):        """ Continuosly listens to user input """        while True:            query=input()            processor.Processor().process(query)-# bot.pyimport listenerclass Bot:    def __init__(self, listener):        listener.listen()Bot(listener.Listener())
查看完整描述

2 回答

?
ABOUTYOU

TA贡献1812条经验 获得超5个赞

我认为你可以重新考虑这个设计,记住两个概念:

  1. Python 多重继承来设计你的类。

  2. 使用 super() 和 Python 的方法解析顺序 (MRO) 在测试代码中注入类依赖项的模拟。

关于第一点,您的类将如下所示:

# core.py


class Core:

    def decide(self, data):

        """ Makes decisions based on input data """


        # blah blah


# tokenizer.py


class Tokenizer:

    def tokenize(self, query):

        """ Tokenize the input query string """

        return query


# processor.py

# from tokenizer import Tokenizer

# from core import Core


class Processor(Core, Tokenizer):

    def process(self, query):

        """ 

        Send the input query to tokenizer 

        tokenized input is send to the Core module for decision making

        """

        tokenized_input = super().tokenize(query)

        super().decide(tokenized_input)


# listener.py

# from processor import Processor


class Listener(Processor):

    def listen(self):

        """ Continuosly listens to user input """

        while True:

            query=input()

            super().process(query)


# bot.py

#from listener import Listener


class Bot(Listener):

    def start_listener(self):

        super().listen()



Bot().start_listener()

通过这种OO设计和super()的使用,我们可以利用MRO在类的依赖项中注入模拟,我将展示如何在SUT(被测主题)中为其依赖项注入模拟。


机器人和处理器的示例:


class MockCore(Core):

    def decide(self, data):

        """ Here you can implement the behavior of the mock """


class MockTokenizer(Tokenizer):

    def tokenize(self, query):

        """ Here you can implement the behavior of the mock """

        return query


class ProcessorSut(Processor, MockCore, MockTokenizer):

    'Here we are injecting mocks for Processor dependencies'


class Bot(Listener):

    def start_listener(self):

        super().listen()


class MockListener(Listener):

    def listen(self):

        """ Here you can implement the behavior of the mock """

        return


class BotSut(Bot, MockListener):

    'Here we are injecting a mock for the Listener dependency of Bot'

看到我们的SUT类的MRO,我们可以理解为什么多重继承和super()的使用允许我们以这种方式注入模拟。


机器人和处理器解决方案的结果 MRO:


Help on class BotSut in module __main__:


class BotSut(Bot, MockListener)

 |  Here we are injecting a mock for the Listener dependency of Bot

 |  

 |  Method resolution order:

 |      BotSut

 |      Bot

 |      MockListener

 |      Listener

 |      Processor

 |      Core

 |      Tokenizer

 |      builtins.object

...

...

Help on class ProcessorSut in module __main__:


class ProcessorSut(Processor, MockCore, MockTokenizer)

 |  Here we are injecting mocks for Processor dependencies

 |  

 |  Method resolution order:

 |      ProcessorSut

 |      Processor

 |      MockCore

 |      Core

 |      MockTokenizer

 |      Tokenizer

 |      builtins.object


查看完整回答
反对 回复 2022-09-13
?
杨__羊羊

TA贡献1943条经验 获得超7个赞

您可以将实用程序类设置为类属性:


class Core:

    def decide(self, data):

        return False



class Tokenizer:

    def tokenize(self, query):

        return []  # ...



class Processor:

    tokenizer_class = Tokenizer

    core_class = Core


    def process(self, query):

        tokenized_input = self.tokenizer_class().tokenize(query)

        return self.core_class().decide(tokenized_input)



class Listener:

    processor_class = Processor


    def listen(self):

        while True:

            query = input()

            self.processor_class().process(query)

然后,您可以使用测试框架的模拟/补丁功能,例如pytest的:monkeypatch


def test_foo(monkeypatch):

    monkeypatch.setattr(Processor, 'tokenizer_class', FooTokenizer)

    # etc...


查看完整回答
反对 回复 2022-09-13
  • 2 回答
  • 0 关注
  • 107 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信