<p>Python中鼓励使用Duck类型的原因之一是,有人可能会包装您的一个对象,然后它看起来会是错误的类型,但仍然有效。</p>
<p>下面是包装对象的类的示例。<code>LoggedObject</code>在所有方面都像它包装的对象一样,但是当您调用<code>LoggedObject</code>时,它会在执行调用之前记录调用。</p>
<pre><code>from somewhere import log
from myclass import A
class LoggedObject(object):
def __init__(self, obj, name=None):
if name is None:
self.name = str(id(obj))
else:
self.name = name
self.obj = obj
def __call__(self, *args, **kwargs):
log("%s: called with %d args" % (self.name, len(args)))
return self.obj(*args, **kwargs)
a = LoggedObject(A(), name="a")
a(1, 2, 3) # calls: log("a: called with 3 args")
</code></pre>
<p>如果显式测试<code>isinstance(a, A)</code>,它将失败,因为<code>a</code>是<code>LoggedObject</code>的实例。如果你让鸭子打字就行了,这就行了。</p>
<p>如果有人错误地传递了错误类型的对象,则会引发<code>AttributeError</code>之类的异常。如果显式地检查类型,异常可能会更清楚,但我认为总体来说,这种情况是duck类型的胜利。</p>
<p>有时候你真的需要测试这个类型。我最近学到的一点是:当你在编写与序列一起工作的代码时,有时你真的需要知道你是否有一个字符串,或者它是任何其他类型的序列。考虑一下:</p>
<pre><code>def llen(arg):
try:
return max(len(arg), max(llen(x) for x in arg))
except TypeError: # catch error when len() fails
return 0 # not a sequence so length is 0
</code></pre>
<p>这应该返回序列的最长长度,或者任何嵌套在其中的序列。它起作用:</p>
<pre><code>lst = [0, 1, [0, 1, 2], [0, 1, 2, 3, 4, 5, 6]]
llen(lst) # returns 7
</code></pre>
<p>但是如果调用<code>llen("foo")</code>,<em>它将永远递归,直到堆栈溢出。</p>
<p>问题是字符串有一个特殊的属性,即使从字符串中取最小的元素,它们总是像一个序列;一个字符串仍然是一个序列。因此,如果没有字符串的显式测试,我们就不能编写llen()。</p>
<pre><code>def llen(arg):
if isinstance(arg, str): # Python 3.x; for 3.x use isinstance(arg, basestring)
return len(arg)
try:
return max(len(arg), max(llen(x) for x in arg))
except TypeError: # catch error when len() fails
return 0 # not a sequence so length is 0
</code></pre>