OpenerDirector是怎么把这些handler分类的
上篇文章说到,在build_opener中只是调用了OpenerDirector的add_handler方法,并不是直接操作的属性来完成handler的添加的。那么来看看OpenerDirector.add_handler具体做了些什么工作。
这个函数的作用其实很简单,就是传进来的Handler进行分类。既然是分类那就要有分类的依据了,那么分类的依据是什么呢?
看一下这四个属性就知道了:
self.handle_open = {} self.handle_error = {} self.process_response = {} self.process_request = {}它要把你传递来的Handler分别放到这四个字典中,显然对应的Handler就应该会包含open或者error或者resonse或者request的属性或者方法,说到这里来看下代码就了解了:
def add_handler(self, handler): ''' 根据协议添加handler到不同的字典中, 所有的handler都会存放到handlers这个list中。 ''' if not hasattr(handler, "add_parent"): raise TypeError("expected BaseHandler instance, got %r" % type(handler)) added = False for meth in dir(handler): #过滤掉命名恰好和下面判断规则一致的函数。 # 但这几个其实并不是需要的函数。 if meth in ["redirect_request", "do_open", "proxy_open"]: # oops, coincidental match continue i = meth.find("_") protocol = meth[:i] condition = meth[i+1:] if condition.startswith("error"): j = condition.find("_") + i + 1 kind = meth[j+1:] try: kind = int(kind) except ValueError: pass lookup = self.handle_error.get(protocol, {}) self.handle_error[protocol] = lookup elif condition == "open": kind = protocol lookup = self.handle_open elif condition == "response": kind = protocol lookup = self.process_response elif condition == "request": kind = protocol lookup = self.process_request else: continue handlers = lookup.setdefault(kind, []) if handlers: bisect.insort(handlers, handler) else: handlers.append(handler) added = True if added: #bisect.insort(self.handlers, handler) handler.add_parent(self)从这个函数我们可以看到关于分类的具体处理过程,其实就是通过遍历这个handler对象的所有方法,然后根据其中是否存在某指定方法来进行分类的。比如HttpHanlder中有http_open这个方法,那么就会被放到handle_open中。至于代码中其他的操作都是对一些基本属性和对那些会产生冲突的方法的过滤。
最后存入字典的key也需要注意一下,这个key就是对应的协议,而此事的值并不是单独的handler对象,而是一个列表,这说明,如果有两个Handler(比如AHandler和BHandler)中含有同样的http_open方法,如果是这样的话,在后面要处理对应的http open请求的话就需要通过这两个handler依次处理。
最后还有一个被我注释掉的一句代码,这个其实urllib2作者也有写注释,handlers这个列表只是为了保持兼容。
每个handler类都继承同一个BaseHandler,拥有add_parent方法,这个方面的作用是为了在handler中同OpenerDirector进行通信。
这个函数的功能也就这些了,不过从这里我看到了另外的东西,就是:约定。
所有的这些都是基于一个约定,约定handler中的关键函数一定要是网络协议加上对应的方法,约定每个handler必须有一个add_parent方法,以及其他的一些约定。
了解这些约定的目的一个是方便理解urllib2在处理url的过程,另外一个就是方便自己以后编写一些扩展handler。
不知道说得是不是够清晰,我自己觉得还不是很清晰,或许在写到最后一篇的时候才会真正清晰。
这一篇和上一篇把urlopen中的构建opener对象的过程都学习了一下,下一篇就来学习这两篇构建opener是怎么处理你给定的url的。