常用的 Duck-Typing 约定

你可能听说过一个古老的谚语,“if it walk like a duck, and talks like a duck, it's a duck”“如果走路像鸭子,说话像鸭子,它就是一个鸭子”莎士比亚在他写“罗密欧与朱丽叶”时,按照这个意思排演过有点浪漫的戏剧。。。。(待翻译)

在 python中,和其他的一些语言中,这个概念被扩展,用来指向对象类型。不是依赖一个对象已经定义好的一些基类或接口,他只要简单的实现期望表现的必要 属性和方法。在python中常见的例子就是“file-like object”(文件类对象),这种对象,只要至少实现和python file对象相同的方法即可。按照这个方法,许多库都可以返回他们自己的对象,这些对象可以传递到其他有能力操作文件对象的函数中去,比如读取,压缩,加 密,从internet下载或其他的操作。

同时,像其他语言的接口,python对象一次可以扮演几个duck类型。这是不寻常的做法, 举例来说,有一个对象,某些方面可以像字典的行为,而其他的时候行为又像列表。Django的HttpResponse对象就是这样的展现这两个行为,像 模仿了一个打开的文件对象。

在Django里,许多特性利用duck-typing,不提供特殊的基类,相反,每个特性定义一个排序的约定,一组对象要正确处理的必备的方法和属性。许多这种约定在django官方文档中都有展示。本书也会多次涉及。你会看到许多使用这种技巧而展现出来的能力。

下面的部分描述一些常见的python约定,在django中贯穿始终,在任何大一些的python库都的确存在。

Callable

python允许代码可以有许多途径被运行,任何东西都能像典型的函数标识可调用的一样,按相同的方式执行。所有函数,类,方法都是自动可调用的,像期望的一样,但是任意对象类实例也能被标识为可调用,只要提供一个单一方法。

__calll__(self[, ...])

当实例化对象像函数一样调用时,这个方法就开始执行。它和其他的成员函数很象,不同的地方就是被调用的方式。

>>> class Multiplier(object):
...     def __init__(self, factor):
...         self.factor = factor
...     def __call__(self, value):
...         return value * self.factor
...
>>> times2 = Multiplier(2)
>>> times2(5)
10
>>> times2(10)
20
>>> times3 = Multiplier(3)
>>> times3(10)
30

python也提供了一个内置函数来判定可调用(callable)的对象。 callable()函数只有一个参数,返回True或False。判断对象是否像函数一样可调用。

>>> class Basic(object):
...     pass
...
>>> class Callable(object):
...     def __call__(self):
...         return "Executed!"
...
>>> b = Basic()
>>> callable(b)
False
>>> c = Callable()
>>> callable(c)
True

字典

字 典是单一对象中的键值映射。大多数编程语言都有相同形式的字典类型,其他语言称之为“hashes", "maps"或"associative arrays(关联数组)",除了简单通过指定键来存取值,python的字典提供了许多方法,一边更好的操作底层的映射。为了像一个真正的字典的表现行 为,一个对象需要提供一些方法,文档参见python library reference。

__contains__(self, key)

通过使用in操作符, 如果指定的键存在于底层的映射中,则返回True,否则返回False。它从不返回异常。

__getitem__(self, key)

如果存在的华,则返回被指定键引用的值。如果键不在底层的映射中,则抛出一个异常KeyError。

__setitem__(self, key, value)

通过指定的键来保存指定的值,保存之后准备引用它。如果有一个映射存在,可用相同的键覆盖已经存在的值。

>>> class CaseInsensitiveDict(dict):
...     def __init__(self, **kwargs):
...         for key, value in kwargs.items():
...             self[key.lower()] = value
...     def __contains__(self, key):
...         return super(CaseInsensitiveDict, self).__contains__(key.lower())
...     def __getitem__(self, key):
...         return super(CaseInsensitiveDict, self).__getitem__(key.lower())
...     def __setitem__(self, key, value):
...         super(CaseInsensitiveDict, self).__setitem__(key.lower(), value)
...
>>> d = CaseInsensitiveDict(SpAm='eggs')
>>> 'spam' in d
True
>>> d['SPAM']
'eggs'
>>> d['sPaM'] = 'burger'
>>> d['SpAm']
'burger

字典也可以用作迭代使用,当代码循环字典的内容时,键用作列表。更多信息参见即将介绍的"Iterables"(迭代)。