因为Python是一门动态语言,Duck Typing的概念遍布其中,所以其中的Concept并不以类型的约束为载体,
而另外使用称为协议的概念。所谓协议,就是在Python中就是需要谰用某个方法,
正好就有这个方法。比如在字符串格式化中,如果有占位符%s.
那么按照字符串转换的协议,Python会去自动地调用相应对象的__str__()
方法。
class Object(object):
def __str__(self):
print('called __str__')
return super(Object,self).__str__()
o = Object()
print("%s" %o)
called __str__ <__main__.Object object at 0x7f5fd11593a0>
这倒数第二行就是明证。除了 __str__
外,还有其他的方法,
比如__repr__()
、__init__()
、__long__()
、__float__()
、__nonzero__()
等,
统称类型转换协议,除了类型转换协议之外,还有许多其他协议。
常用的对象协议
1)用以比较大小的协议,这个协议依赖于__cmp__()
方法,与C语言库函数 cmp
类似,
当两者相等时,返回CK,当 self<other
时返回负值,反之返回正值。因为它的这种复杂性,
所以Python又有__eq__()
、__ne__()
、__lt__()
、__gt__()
等方法来实现相等、
不等、小于和大于的判定。这也就是Python对=、!=、<和>等操作符的进行重载的支撑机制。
2)数值类型相关的协议,这一类的函数比较多,如表6-2所示。
基本上,只要实现了表6-2中的几个方法,基本上就能够模拟数值类型了。
不过还需要提到一个Python中特有的槪念:反运算,别被吓着,其实非常简单。
以加法为例, something+other
,调用的是 something
的__add__()
方法,
如果 something
没有__add__()
方 法怎么办呢?调用other.__add__()
是不对的,
这时候Python有一个反运算的协议,它会去査看 other
有没有__radd__()
方法,
如果有,则以 something
为参数调用之。类似__radd__()
的 方法,
所有的数值运箅符和位运算符都是支持的,规则也是一律在前面加上前缀r即可,在此不再细表。
3)容器类型协议。容器的协议是非常浅显的,既然为容器,那么必然要有协议査询内含多少对象.
在Python中,就是要支持内置函数 len()
,通过__len__()
来完成,一目了然。
而 __getitem__()
、__setitem__()
、__delitem__()
则对应读、写和删除,
也很好理解。__iter__()
实现了迭代器协议,而__reverSed__()
则提供对内置函数 reversed()
的支持。
容器类型中最有特色的是对成员关系的判断符 in
和 not in
的支持,这个方法叫__contains__()
,
只要支持这个函数就能够使用 in
和 not in
运算符了。
4)可调用对象协议。所谓可调用对象,即类似函数对象,能够让类实例表现得像函数一样, 这样就可以让每一个函数调用都有所不同。
class Functor(object):
def __init__(self,context):
self._context = context
def __call__(self):
print('de something with %s' %self._context)
lai_functor = Functor('lai')
yong_functor = Functor('yong')
lai_functor()
de something with lai
yong_functor()
de something with yong
5)与可调用对象差不多的,还有一个可哈希对象,它是通过__hash__()
方法来支持 hash()
这个内置函数的,
这在创建自己的类型时非常有用,因为只有支持可哈希协议的类型才能作为 dict
的键类型
(不过只要继承自 object
的新式类默认就支持了)。
6)前面的文档谈对描述符协议和属性交互协议(__getattr__()
、__setattr__()
、__delattr()
),
那么剩下来还值得一谈的就是上下文管理器协议了,也就是对 with
语句的支持,
这个协议通过__enter__()
和__exit__()
两个方法来实现对资源的清理,
确保资源无论在什么情况下都会正常清理。
class Closer:
'''通过with语句和一个close方法来关闭一个对象'''
def __init__(self,obj):
self.obj = obj
def __enter__(self):
return self,obj # bound to target
def __exit__(self,exception_type,exception_val, trace):
try:
self.obj.close()
except AttributeError: # obj isn't closable
print ('Not closable.')
return True #exception handled successfully
对于实现了这两个方法的 Closer
类,可以如下使用它:
from ftplib import FTP
with Closer(FTP('ftp.somesite.com')) as conn:
conn.dir()
conn.dir()
可以看到第二次调用 conn.dir()
已经没有输出,这是因为这个FTP连接会话已被关闭的缘故。
与这里 Closer
类似的类在标准库中已经存在,就是 contextlib
里的 closing
。
至此,常用的对象协议就讲完了,只要活学活用这些协议,就能够写出更为Pythonic的 代码。 不过也要注意,协议不像C++、Java等语言中的接口,它更像是声明,没有语言上的 约束力, 需要大家共同遵守。