>>> from env_helper import info; info()
页面更新时间: 2023-12-27 10:44:38
运行环境:
Linux发行版本: Debian GNU/Linux 12 (bookworm)
操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
Python版本: 3.11.2
6.10. 熟悉Python对象协议¶
因为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)
这倒数第二行就是明证。除了
__str__
外,还有其他的方法,比如__repr__()
、__init__()
、__long__()
、__float__()
、__nonzero__()
等,统称类型转换协议,除了类型转换协议之外,还有许多其他协议。
1)用以比较大小的协议,这个协议依赖于__cmp__()
方法,与C语言库函数cmp类似,
当两者相等时,返回CK当setf<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__()
则提供对内S函数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()
>>> yong_functor()
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等语言中的接口,它更像是声明,没有语言上的 约束力,需要大家共同遵守。