如何从Python背景了解接口

时间:2019-03-25 15:40:17

标签: python interface

我很难理解C#中接口的使用。 这可能是因为我来自Python,而该Python并未在其中使用。

我不理解其他解释,因为它们不能完全回答我有关接口的问题。例如The purpose of interfaces continued

据我了解,Interfaces告诉类它可以做什么,而不是怎么做。这意味着在某个时候必须告诉类如何执行方法。

如果是这种情况,接口的意义何在?为什么不只在类中定义方法?

我看到的唯一好处是清楚地说明了哪些类可以/不能做什么,但是以不使用DRY代码为代价?

我知道Python不需要接口,我认为这限制了我的理解,但无法完全弄清楚为什么。

2 个答案:

答案 0 :(得分:1)

它在python中以抽象类的形式使用(通常)。

目的各不相同,在Java中,它们解决了多重继承问题;在python中,它们充当了2个类之间的契约。

A类做某事,而其中的一部分则涉及B类。B类可以通过多种方式实现,因此,不必使10个不同的类和HOPE正确使用,而应使它们从抽象类继承(接口),并确保它们必须实现定义为抽象的所有方法。 (请注意,如果它们不采用任何一种在构建时崩溃的方法,则当您安装软件包时,而不是在中,大型项目中非常重要的运行时)。

您还知道实现这些方法的ANY类将与使用它的类一起使用。这听起来很琐碎,但由于它意味着您可以将部分代码外包,并且可以连接其余的代码并与之一起工作,因此业务方面对此并不满意。

  

据我了解,Interfaces告诉类它可以做什么,而不是怎么做。这意味着在某个时候必须告诉类如何执行方法。

实际上,他们告诉它必须做什么,以及我们不关心他们如何做。

  

如果是这种情况,接口的意义何在?为什么不只在类中定义方法?

这不是重点,但是是的,您绝对需要在继承自接口的类中定义方法。

让我们给您一个更具体的例子。

想象一下,您有一个运行某些任务的python框架。这些任务可以在本地运行(在运行python框架的同一台计算机上),它们可以在分布式系统上运行,通过将它们提交给某个中央调度程序,它们可以在docker容器中运行,在Amazon Web services上。你明白了。

您所需要的只是一个具有run_task方法的接口(python中的抽象类),具体取决于您使用哪个。

例如:

class Runner:
    __metaclass__ = abc.ABCMeta

    @abstractmethod
    def run_task(self, task_command):
         return


class LocalRunner(Runner):
    def run_task(self, task_command):
        subprocess.call(task_command)

class SlurmRunner(Runner):
    def run_task(self, task_command):
        subprocess.call('sbatch ' + task_command)

现在很重要,因为您可能会问为什么^ $ ^ $%我需要所有这些 并发症? (如果yopur项目足够小,您可能不会知道,但是根据您几乎必须开始使用这些功能的大小,会有一个断点。)

仅使用运行器的类需要了解接口,例如您有一个Task类,该类可以将任务的执行委托给TaskRunner,因为您不必在乎哪个实现,它们在某种意义上是多态的。

class Task:
    def __init__(self, task_runner):
        self.task_runner = task_runner
        self.task_command = 'ls'

    def run_this_task(self):
        self.task_runner.run_task(self.task_command)

并且,如果您是老板可以告诉您的一些程序员,我需要一个新类在AWS上执行命令,将其分配给命令,并实现task_runner方法,那么您无需了解其他任何内容代码,您可以将其作为一个完全隔离的部分来实现(这是外包部分,现在您可以有100个人指定100个不同的部分,他们不需要了解任何代码,只需要了解接口即可)

答案 1 :(得分:0)

class Cat:
    def meow(self):
        print('meow')

def feed(cat):
    cat.moew()  # he thanks the owner

tom = Cat('Tom')
feed(tom)

C Sharp具有静态类型系统。编译器需要知道该类具有哪些方法。这就是为什么我们必须为每个变量设置类型的原因:

def feed(cat: Cat):
    cat.moew()  # he thanks the owner

但是,如果我们必须编写代码并且不知道变量必须具有的确切类型怎么办?

def feed(it):
    it.thank_owner()

此外,我们必须假设我们的函数将用于各种类。别忘了我们必须让编译器知道每个变量的类型!该怎么办?解决方案:

class Pet:  # an interface
    def thank_owner(self):
        raise NotImplementedError()

def feed(it: Pet):
    it.thank_owner()

但是与Cat怎么办?解决方案:

class Cat(Pet):  # inherits the interface Pet
    def thank_owner(self):
        print('meow')  # or self.meow() if we want to avoid big changes and follow DRY rule at the same time

tom = Cat('Tom')
feed(tom)

现在,我们可以轻松添加新宠物。我们不必重写代码。

class Dog(Pet):
    def thank_owner(self):
        print('woof')

beethoven = Dog('Beethoven')
feed(beethoven)  # yes, I use the same function. I haven't changed it at all!

请注意,我们在feed()Pet之后创建了此类。之前编写代码时,我们不要考虑Dog,这一点很重要。我们对此并不感到好奇。但是,当我们需要扩展代码时,我们没有遇到任何问题。