如何从Python背景中理解接口

How to understand interfaces from a Python background

我很难理解C中接口的使用。这很可能是因为我来自于Python,它在那里没有被使用。

我不理解其他解释,因为它们没有完全回答我关于接口的问题。例如,接口的目的继续

根据我的理解,接口告诉类它可以做什么,而不是如何做。这意味着在某个时刻,必须告诉类如何执行这些方法。

如果是这样,那么接口中的要点是什么?为什么不在类中定义方法呢?

我看到的唯一好处是让它清楚什么类可以/不能做,但代价是不干代码?

我知道python不需要接口,我认为这限制了我的理解,但我不太明白为什么。


它在Python中使用,形式(通常)是抽象类。

目的不同,在Java中,它们解决多重继承,在Python中,它们充当2个类之间的契约。

类A做了一些事情,其中的一部分涉及类B。类B可以通过多种方式实现,因此,您不必生成10个不同的类并希望它们能被正确使用,而是使它们继承自抽象类(接口),并确保它们必须实现定义为抽象的所有方法。(请注意,如果它们没有实现任何方法,那么在构建时、安装包时、而不是运行包时,这些方法都会崩溃,这在中型/大型项目中非常重要)。

您还知道实现这些方法的任何类都将与使用它的类一起工作。这听起来很琐碎,但是业务部门喜欢它,因为这意味着你可以将部分代码外包出去,它将与你的其他代码连接并工作。

From what I understand, Interfaces tell a class what it can do, not how to do it. This means at some point the class has to be told how to do the methods.

他们实际上告诉它必须做什么,至于他们怎么做,我们不在乎。

If this is the case, what's the point in interfaces? Why not just have the definition of the method in the class?

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

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

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

您所需要的只是一个接口(Python中的抽象类),它有一个run-task方法,至于您要使用哪个方法取决于您自己。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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项目足够小的话,您可能不会这样做,但是根据您几乎必须开始使用这些东西的大小,有一个断点)。

使用runner的类只需要理解接口,例如,您有一个任务类,该类可以将任务的执行委托给task runner,至于哪个任务是实现的,您不在乎,它们在某种意义上是多态的。

1
2
3
4
5
6
7
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上执行命令,你将它作为一个命令,它实现了一个任务运行方法,然后你不需要知道其他代码的任何信息,你可以把这个位作为一个完全独立的部分来实现(这是外包部分,现在你可以有100个人了)Le Designign 100个不同的P[IECES,他们不需要知道任何关于代码的信息,只需要知道接口)。


1
2
3
4
5
6
7
8
9
class Cat:
    def meow(self):
        print('meow')

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

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

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

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

但是如果我们必须编写代码,并且不知道变量必须具有什么类型呢?

1
2
def feed(it):
    it.thank_owner()

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

1
2
3
4
5
6
class Pet:  # an interface
    def thank_owner(self):
        raise NotImplementedError()

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

但是猫怎么办呢?解决方案:

1
2
3
4
5
6
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)

顺便说一下,我们现在可以轻松地添加新宠物了。我们不必重写代码。

1
2
3
4
5
6
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。我们对此并不好奇。但是,当需要扩展代码时,我们没有遇到问题。