Jef*_*ose 43 python oop interface inversion-of-control
灵感来自一个很好的问题(和一堆很棒的答案).
语句"针对接口的代码,而不是对象"在Python中是否有任何意义?
我正在寻找像原始问题中的答案,但有Python片段和想法.
kin*_*all 60
"对接口而不是对象的代码"在Python中没有字面意义,因为该语言没有接口功能.该粗糙的Python相当于是"使用鸭打字." 如果你想看一个对象是否是一个鸭子,换句话说,你应该检查它是否有一个quack()
方法,或者更好地尝试quack()
并提供适当的错误处理,而不是测试它是否是一个实例Duck
.
Python中常见的duck类型是文件(嗯,实际上是类文件对象),映射(类似对象),dict
callables(类似函数的对象),sequence(类似对象)list
和iterables(你可以迭代的东西,可以是容器或发电机).
例如,想要文件的Python功能通常会乐于接受实现file
其所需方法的对象; 它不需要从file
类中派生出来.例如,要将对象用作标准输出,它需要的主要write()
方法是一种方法(也许flush()
并且close()
,实际上不需要做任何事情).类似地,callable是具有__call__()
方法的任何对象; 它不需要从函数类型派生(事实上,你不能从函数类型派生).
你应该采取类似的方法.检查您要对对象执行的操作所需的方法和属性.更好的是,记录您的期望并假设任何调用您的代码的人都不是完全的doofus.(如果他们给你一个你不能使用的对象,他们肯定会从他们得到的错误中快速地找出它.)仅在必要时测试特定类型.这是必要的时间,这就是为什么Python的给你type()
,isinstance()
和issubclass()
,但要小心他们.
Python的鸭子类型等同于"对接口而不是对象的代码",因为建议您不要让代码过于依赖对象的类型,而是要看它是否具有您需要的接口.不同之处在于,在Python中,"接口"只是指提供特定行为的对象的非正式属性和方法捆绑,而不是特定命名的语言构造interface
.
您可以使用该abc
模块在某种程度上形式化Python"接口" ,这允许您使用您期望的任何条件声明给定类是给定"抽象基类"(接口)的子类,例如"它具有属性color
,tail_length
,和quack
,并且quack
可以赎回." 但这仍然比具有接口功能的静态语言严格得多.
Pau*_*ine 29
要理解Python中的接口,您必须了解duck-typing.从Python 词汇表中:
duck-typing:一种编程风格,它不会查看对象的类型以确定它是否具有正确的接口; 相反,简单地调用或使用方法或属性("如果它看起来像鸭子,像鸭子一样嘎嘎叫,它必须是鸭子.")通过强调接口而不是特定类型,精心设计的代码通过允许改进其灵活性多态替换.Duck-typing避免使用type()或isinstance()进行测试.(但请注意,鸭子类型可以用抽象基类来补充.)相反,它通常使用hasattr()测试或EAFP编程.
Python鼓励对接口进行编码,只是它们不是按照惯例强制执行的.像iterables,callables或文件接口这样的概念在Python中非常普遍 - 以及依赖于map,filter或reduce等接口的内置函数.
小智 18
接口意味着您希望某些方法在对象中呈现和标准化; 这是接口或抽象基类的要点,或者您希望考虑的任何实现.
例如(Java),可能有一个用于对称加密的接口,如下所示:
public interface cipher
{
public void encrypt(byte[] block, byte[] key);
public void decrypt(byte[] block, byte[] key);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以实现它:
public class aes128 implements cipher
{
public void encrypt(byte[] block, byte[] key)
{
//...
}
public void decrypt(byte[] block, byte[] key)
{
//...
}
}
Run Code Online (Sandbox Code Playgroud)
然后可以像这样声明一个对象:
cipher c;
Run Code Online (Sandbox Code Playgroud)
我们在这做了什么?好吧,我们创建了这个对象,c
其类型必须与接口的类型相匹配.c
可以引用与此接口匹配的任何内容,因此下一个阶段将是:
c = new aes128();
Run Code Online (Sandbox Code Playgroud)
您现在可以调用您希望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
Run Code Online (Sandbox Code Playgroud)
如果你想使用它,并且你不确定你传递的对象是什么,只是尝试使用它:
c = aes128()
try:
c.encrypt(someinput, someoutput)
except:
print "eh? No encryption method?!"
Run Code Online (Sandbox Code Playgroud)
在这里,raise
如果方法存在,你依赖于c.encrypt的实现,如果它无法处理传递的内容.当然,如果c
是一个字符串类型,因此不是你需要的正确类型,它也会自动抛出,你会抓住(希望).
简而言之,键入一种编程形式,使得您必须遵守接口规则,另一种形式是说您甚至不需要将它们写下来,您只需相信如果它没有错误,它就可以工作.
我希望能告诉你两者之间的实际区别.