注入符合公共接口的适配器的Pythonic方式

注入符合公共接口的适配器的Pythonic方式,python,Python,我正在使用boto3为AWS设置一些路由 在AWS环境中,我们希望有一个类负责将路由插入EC2 VPC。在非AWS环境中,它将执行其他操作 在传统的OOP方法中,我会有一个接口来定义一个annound\u route方法,例如,并创建AWSRouting或FoobarRouting的具体实例,等等 用于说明该场景的伪代码: interface Routing function announceRoute() class AWSRouting implements Routing cla

我正在使用boto3为AWS设置一些路由

在AWS环境中,我们希望有一个类负责将路由插入EC2 VPC。在非AWS环境中,它将执行其他操作

在传统的OOP方法中,我会有一个接口来定义一个
annound\u route
方法,例如,并创建AWSRouting或FoobarRouting的具体实例,等等

用于说明该场景的伪代码:

interface Routing
   function announceRoute()

class AWSRouting implements Routing

class RouteAnnouncer
   function constructor(Routing routing)
我会根据一些环境变量或配置选项将适当的路由实例注入到播音器中

通过依赖注入实现这种适配器模式的pythonic方法是什么?到目前为止,我还没有看到任何为构造函数或setter的依赖项定义类型的方法


到目前为止,我得到的是:

class RouteAnnouncer:
    def __init__(self, adapter):
        self.adapter = adapter

    def announce_route(self, ip_addr):
        self.adapter.announce_route(ip_addr)


class AnnounceRouteContract:
    def announce_route(self, ip_addr):
        raise NotImplementedError


class AWSRouting(AnnounceRouteContract):
    def announce_route(self, ip_addr):
        pass


class StandardRouting(AnnounceRouteContract):
    def announce_route(self, ip_addr):
        pass

因此,我认为我真正缺少的是如何告诉代码用户,
routeAnouncer
中的适配器是
announceRouteContact
的一个实例,您不能强迫类用户通过本机python只放置特定类的实例(在Python3.6中,它似乎是实现的)。您只需给用户一个提示,这样IDE就可以在存在非预期类的实例时进行投诉:

class RouteAnnouncer:
    def __init__(self, adapter):
        """
        :param AnnounceRouteContract adapter: Route adapter
        self.adapter = adapter
此外,还将合同抽象化,并使用
公布路线
方法强制用户实施


您还可以使用内置函数检查适配器是否是预期契约的实例

您不能强制类用户通过本机python只放置特定类的实例(在python 3.6中,它似乎已实现)。您只需给用户一个提示,这样IDE就可以在存在非预期类的实例时进行投诉:

class RouteAnnouncer:
    def __init__(self, adapter):
        """
        :param AnnounceRouteContract adapter: Route adapter
        self.adapter = adapter
此外,还将合同抽象化,并使用
公布路线
方法强制用户实施


此外,您还可以使用内置函数检查适配器是否是预期契约的实例。在python世界中,无法指定参数类型。我认为一种合适的方法是使用断言检查内部函数中的适配器类型

class RouteAnnouncer:
    def __init__(self, adapter):
        assert isinstance(adapter, AnnounceRouteContract)
        self.adapter = adapter

在python世界中,无法指定参数类型。我认为一种合适的方法是使用断言检查内部函数中的适配器类型

class RouteAnnouncer:
    def __init__(self, adapter):
        assert isinstance(adapter, AnnounceRouteContract)
        self.adapter = adapter

你不需要做任何像创建一堆“接口”这样愚蠢的事情。在Python中,接口由您的行为决定。通常被称为“鸭子打字”,因为如果你看起来像鸭子,走路像鸭子,嘎嘎叫像鸭子,那么你一定是鸭子

我们不在乎你是否真的是一个穿着鸭子服装的人,或者是一张鸭子的纸板,或者是一只机器鸭子。这完全无关

在您的案例中,您真正关心的似乎是
announce\u route(ip\u addr)
是您可以调用的东西。当然,最终你希望它能真正建立起路线,但你知道,这就是测试的目的

您可能想做的是创建一个类似以下内容的工厂:

class MyRouteAnnouncer:
    def __init__(self):
        ...  # whatever you need to do

    def announce_route(self, ip_addr):
        ...  # do what you need to do


def get_router():
    if env.is_aws():
        return boto3
    elif env.is_something_else():
        return MyRouteAnnouncer()
    else:
        raise Exception('Unknown environment')

然后,调用
get\u router()
函数的任何程序只需获取相应的路由器并在其上调用
announce\u route(ip\u addr)

您不需要做任何像创建一堆“接口”这样愚蠢的事情。在Python中,接口由您的行为决定。通常被称为“鸭子打字”,因为如果你看起来像鸭子,走路像鸭子,嘎嘎叫像鸭子,那么你一定是鸭子

我们不在乎你是否真的是一个穿着鸭子服装的人,或者是一张鸭子的纸板,或者是一只机器鸭子。这完全无关

在您的案例中,您真正关心的似乎是
announce\u route(ip\u addr)
是您可以调用的东西。当然,最终你希望它能真正建立起路线,但你知道,这就是测试的目的

您可能想做的是创建一个类似以下内容的工厂:

class MyRouteAnnouncer:
    def __init__(self):
        ...  # whatever you need to do

    def announce_route(self, ip_addr):
        ...  # do what you need to do


def get_router():
    if env.is_aws():
        return boto3
    elif env.is_something_else():
        return MyRouteAnnouncer()
    else:
        raise Exception('Unknown environment')

然后调用
get\u router()
函数的任何东西都只会得到相应的路由器,并在其上调用
announce\u route(ip\u addr)

您看过了吗?几乎每次您认为需要提供类的类型时,您都错了。接受duck类型。你看了吗?几乎每次你认为你需要提供类的类型时,你都错了。拥抱鸭子打字。谢谢你。这就是我为什么要问的原因——我知道我以前的思维方式可能不适用于python,所以我一直在寻找我应该采取的方法。谢谢。这就是我为什么要问的原因——我知道我以前的思维方式可能不适用于python,所以我一直在寻找我应该采取的方法。