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

时间:2010-12-28 19:55:15

标签: python oop interface inversion-of-control

受到来自here.

的一个很好的问题(以及大量答案)的启发

语句“针对接口的代码,而不是对象”在Python中是否有任何意义?

我正在寻找像Original Question中的答案,但有Python片段和想法。

4 个答案:

答案 0 :(得分:59)

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

Python中常见的duck类型是文件(嗯,实际上是类文件对象),映射(dict - 类似对象),callables(类似函数的对象),sequence(list - like对象)和迭代(你可以迭代的东西,可以是容器或生成器)。

例如,想要文件的Python功能通常会乐于接受实现其所需的file方法的对象;它不需要从file类派生。例如,要将对象用作标准输出,它需要的主要方法是write()方法(可能flush()close(),它们实际上不需要做任何事情) 。类似地,可调用的是具有__call__()方法的任何对象;它不需要从函数类型派生(事实上,你不能从函数类型派生)。

你应该采取类似的方法。检查您要对对象执行的操作所需的方法和属性。更好的是,记录您的期望并假设任何调用您的代码的人都不是完全的doofus。 (如果他们给你一个你不能使用的对象,他们肯定会从他们得到的错误中快速地解决这个问题。)仅在必要时测试特定类型。 有时是必要的,这就是Python为您提供type()isinstance()issubclass()的原因,但要小心它们。

Python的鸭子类型相当于“针对接口而不是对象的代码”,因为建议您不要让代码过于依赖于对象的类型,而是要看它是否具有您需要的接口。不同之处在于,在Python中,“接口”只是指提供特定行为的对象的非正式属性和方法捆绑,而不是专门命名为interface的语言构造。

您可以使用abc模块在​​某种程度上形式化Python“接口”,这允许您使用您想要的任何标准声明给定的类是给定“抽象基类”(接口)的子类,例如“它具有属性colortail_lengthquackquack可以调用。”但这仍然比具有接口功能的静态语言严格得多。

答案 1 :(得分:29)

要了解Python中的接口,您必须了解duck-typing。来自Python glossary

  

duck-typing :一种编程风格,它不会查看对象的类型以确定它是否具有正确的接口;相反,简单地调用或使用方法或属性(“如果它看起来像鸭子,像鸭子一样呱呱叫,它必须是鸭子。”)通过强调接口而不是特定类型,精心设计的代码通过允许改进其灵活性多态替换。 Duck-typing避免使用type()或isinstance()进行测试。 (但请注意,鸭子类型可以用抽象基类来补充。)相反,它通常使用hasattr()测试或EAFP编程。

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

答案 2 :(得分:18)

接口意味着您希望某些方法在对象中出现并标准化;这是接口或抽象基类的重点,或者您希望考虑的任何实现。

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

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;

我们在这做了什么?好吧,我们创建了这个对象c,其类型必须与接口的类型相匹配。 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对raise的实现。当然,如果c是一个字符串类型,因此不是你需要的正确类型,它也会自动抛出,你会抓住(希望)。

简而言之,一种形式的编程是打字的,你必须遵守接口规则,另一种形式是说你甚至不需要写下来,你只需要相信如果它没有错误,它就会工作

我希望能告诉你两者之间的实际区别。

答案 3 :(得分:0)

  

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

正确的引用是“针对接口的程序,而不是实现”。这个原理在Python中的含义与Smalltalk中的原理相同,Smalltalk就是它所源自的语言。

  

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

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