什么是“针对接口,而不是对象的代码”的Python版本?

受到一个很好的问题(以及一堆很好的答案)的启发。

“对一个接口,而不是一个对象的代码”语句在Python中有什么意义吗?

我正在寻找像原始问题的答案,但与Python的片段和想法。

“针对接口而不是对象的代码”在Python中并不具有字面意义,因为该语言没有接口function。 粗略的 Python等价物是“使用鸭子打字”。 如果你想看看一个对象是否是鸭子,换句话说,你应该检查它是否有一个quack()方法,或者更好地尝试quack()并提供适当的error handling,而不是testing它是否它是Duck一个实例。

在Python中常见的鸭子types是文件(嗯,实际上是类文件对象),映射(类似于对象的对象),可调用函数(类似于对象的对象),序列(类list对象)和迭代(你可以遍历的东西,可以是容器或发电机)。

例如,希望文件的Python特性通常很乐意接受实现所需file方法的对象; 它不需要从file类派生。 例如,要使用一个对象作为标准,它需要的主要是一个write()方法(也许是flush()close() ,它们实际上不需要做任何事情)。 类似地,可调用是具有__call__()方法的任何对象; 它不需要从函数types派生(事实上,你不能从函数types派生)。

你应该采取类似的方法。 检查你要用对象做什么的方法和属性。 更好的是,logging你的期望,并假设谁调用你的代码不是一个总的doofus。 (如果他们给你一个你不能使用的对象,他们肯定会很快从错误中得出结论。)只有在必要时才testing特定的types。 这必要的,这就是为什么Python给你type()isinstance()issubclass() ,但要小心。

Python的鸭子打字相当于“针对接口而不是对象的代码”,因为build议您不要让代码过于依赖对象的types,而是要看看它是否具有所需的接口。 不同的是,在Python中,“接口”仅仅意味着提供特定行为的对象的属性和方法的非forms捆绑,而不是特定名称的语言构造。

您可以使用abc模块在某种程度上正式化Python“接口”,它允许您使用任何标准声明给定的类是给定“抽象基类”(接口)的子类,例如“它具有属性colortail_lengthquack ,而quacktail_length 。“ 但是这比具有界面function的静态语言要严格得多。

要理解Python中的接口,您必须了解鸭子input。 从非常Python的词汇表 :

duck-typing :一种编程风格,它不会查看对象的types以确定它是否具有正确的界面; 相反,方法或属性被简单地调用或使用(“如果它看起来像一只鸭子,像一只鸭子,它肯定是一只鸭子。”)通过强调接口而不是特定types,精心devise的代码通过允许多态性取代。 鸭式input避免了使用type()或isinstance()的testing。 (但是请注意,duck-typing可以用抽象基类来补充),而是通常使用hasattr()testing或EAFP编程。

Python鼓励对接口进行编码,只是它们不是强制执行,而是按照惯例。 像iterables,callables或文件接口这样的概念在Python中非常普遍 – 以及依赖于接口(如map,filter或reduce)的内build函数。

一个接口意味着你期望某些方法在对象之间呈现和标准化; 这是一个接口或抽象基类的点,或者你想要考虑的任何实现。

例如(Java),可能有一个对称encryption的接口,如下所示:

 public interface cipher { public void encrypt(byte[] block, byte[] key); public void decrypt(byte[] block, byte[] key); } 

那么你可以实现它:

 public class aes128 implements cipher { public void encrypt(byte[] block, byte[] key) { //... } public void decrypt(byte[] block, byte[] key) { //... } } 

然后可以像这样声明一个对象:

 cipher c; 

我们在这里做了什么? 那么,我们已经创build了这个对象的types必须匹配接口的types。 c可以指任何匹配这个接口的东西,所以下一个阶段是:

 c = new aes128(); 

你现在可以调用你期望cipher方法了。

这是Java。 现在,这是你在Python中做什么:

 class aes128(Object): def __init__(self): pass def encrypt(self, block, key): # here I am going to pass, but you really # should check what you were passed, it could be # anything. Don't forget, if you're a frog not a duck # not to quack! pass 

当你想使用这个,并且你不确定你传递的对象是什么,只是尝试使用它:

 c = aes128() try: c.encrypt(someinput, someoutput) except: print "eh? No encryption method?!" 

在这里,如果c.encrypt的实现存在,那么它将无法处理已传递的内容。 当然,如果c是一个stringtypes,因此不是你需要的正确types,它也会自动抛出,并且你会捕获(希望)。

简而言之,一种编程forms是键入的,你必须遵守接口规则,另一种是说你甚至不需要把它们写下来,只要相信如果没有错误就行了。

我希望能向你们展示两者的实际区别。

“针对界面而不是对象编码”的Python版本是什么?

正确的引用是“ 针对接口的程序,而不是实现 ”。 这个原理在Python中是一样的,它在Smalltalk中就是它的起源语言。

是否声明“代码对一个接口,而不是一个对象”。 在Python中有什么意义?

是。 它在Python中与在(Smalltalk)引用的语言以及其他语言中具有相同的意义。