受到来自here.
的一个很好的问题(以及大量答案)的启发语句“针对接口的代码,而不是对象”在Python中是否有任何意义?
我正在寻找像Original Question中的答案,但有Python片段和想法。
答案 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“接口”,这允许您使用您想要的任何标准声明给定的类是给定“抽象基类”(接口)的子类,例如“它具有属性color
,tail_length
和quack
,quack
可以调用。”但这仍然比具有接口功能的静态语言严格得多。
答案 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)以及其他所有语言中的含义相同。