如何从Python背景理解接口

如何从Python背景理解接口,python,interface,Python,Interface,我很难理解C#中接口的用法。 这很可能是因为我来自Python,但在那里没有使用它 我不理解其他解释,因为它们没有完全回答我关于接口的问题。e、 g 据我所知,接口告诉类它可以做什么,而不是如何做。这意味着在某个时候必须告诉类如何执行这些方法 如果是这样的话,接口有什么意义?为什么不在类中定义方法呢 我看到的唯一好处是清楚地知道类可以/不能做什么,但代价是不干代码 我知道Python不需要接口,我认为这限制了我的理解,但我不太明白为什么。它在Python中以抽象类的形式(通常)使用 目的各不相同

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

我不理解其他解释,因为它们没有完全回答我关于接口的问题。e、 g

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

如果是这样的话,接口有什么意义?为什么不在类中定义方法呢

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


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

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

目的各不相同,在java中它们解决多重继承,在python中它们充当两个类之间的契约

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

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

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!
据我所知,接口告诉类它可以做什么,而不是如何做。这意味着在某个时候必须告诉类如何执行这些方法

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

如果是这样的话,接口有什么意义?为什么不在类中定义方法呢

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

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

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

您所需要的只是一个接口(python中的一个抽象类),该接口有一个run_任务方法,至于使用哪种方法取决于您自己

e、 g:

现在是重要的一点,因为你可能会问为什么我需要所有这些 难题?(如果yopur项目足够小,您可能不知道,但根据大小,有一个断点,您几乎必须开始使用这些东西)

使用运行程序的类只需要理解接口,例如,您有一个任务类,该类可以将任务的执行委托给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种不同的产品,他们不需要知道任何代码,只需要知道接口)

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()
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)
但是如何处理Cat?解决方案:

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

def feed(it: Pet):
    it.thank_owner()
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
,这一点很重要。我们对此并不好奇。但是,当我们需要扩展代码时,我们没有遇到任何问题。

类Pet:#一个接口定义感谢所有者(self):raise NotImplementedError()您的宠物接口将在运行时崩溃,而不是在构建时崩溃,这首先违背了接口的目的。当然是在运行时。它是Python!我想解释一下接口的用途。如果您放置一个抽象类并扩展它,而不实现抽象方法,它将在构建时崩溃(相当于java/C#中的编译时),关键是要使python在这方面更像java或C#(有一个编译步骤非常好,因为它强制键入,python提供了选项,但并不强迫您这样做)。