我们来分析一下tornado.ioloop.IOLoop.instance().start(),学习了tornado后,当启动服务的时候,我一直有一个疑惑,我们看一下源码,IOLoop类中instance()是一个单例模式返回IOLoop实例函数
@staticmethod def instance(): """Returns a global `IOLoop` instance. Most applications have a single, global `IOLoop` running on the main thread. Use this method to get this instance from another thread. To get the current thread's `IOLoop`, use `current()`. """ if not hasattr(IOLoop, "_instance"): with IOLoop._instance_lock: if not hasattr(IOLoop, "_instance"): # New instance after double check IOLoop._instance = IOLoop() return IOLoop._instance
然后我们再看一下IOloop类的start()函数
def start(self): """Starts the I/O loop. The loop will run until one of the callbacks calls `stop()`, which will make the loop stop after the current event iteration completes. """ raise NotImplementedError()
我们发现并未去做实现,是一个抽象方法,或者你可以去python模块包下修改一下源码,如在start()下print("IOLOOP START"),启动我们发现并未打印或者tornado.ioloop.IOLoop.instance().start().im_class看看start()到底属于那个类,我们发现属于EPollIOLoop类(也可以说是EPollIOLoop或其父类的函数)。我们实例化的IOLoop应该是调用IOloop类的实例啊?
再看一下EPollIOLoop在tornado.platform.epoll这个模块中
class EPollIOLoop(PollIOLoop): def initialize(self, **kwargs): super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
比较简单,什么都没有,但是它是PollIOLoop类的子类,我们可以断定那个start()函数属于PollIOLoop
类的,我们就找一下PollIOLoop类的start()函数,比较长就不贴出来了,这个细节不是关注的重点。看一下这个类,我们只能发现是IOLoop的子类实现了它的一些方法,还是从IOLoop来找答案吧,IOLoop类的父类是Configurable在util.py模块
class Configurable(object): """Base class for configurable interfaces. A configurable interface is an (abstract) class whose constructor acts as a factory function for one of its implementation subclasses. The implementation subclass as well as optional keyword arguments to its initializer can be set globally at runtime with `configure`. By using the constructor as the factory method, the interface looks like a normal class, `isinstance` works as usual, etc. This pattern is most useful when the choice of implementation is likely to be a global decision (e.g. when `~select.epoll` is available, always use it instead of `~select.select`), or when a previously-monolithic class has been split into specialized subclasses. Configurable subclasses must define the class methods `configurable_base` and `configurable_default`, and use the instance method `initialize` instead of ``__init__``. """ __impl_class = None __impl_kwargs = None def __new__(cls, **kwargs): base = cls.configurable_base() args = {} if cls is base: impl = cls.configured_class() if base.__impl_kwargs: args.update(base.__impl_kwargs) else: impl = cls args.update(kwargs) instance = super(Configurable, cls).__new__(impl) # initialize vs __init__ chosen for compatiblity with AsyncHTTPClient # singleton magic. If we get rid of that we can switch to __init__ # here too. instance.initialize(**args) return instance
他是一个接口,第一个函数__new__(),首先我们要知道__new__函数的意义(具体解释大家可以查询),__new__函数是在对象实例化的时候执行返回对象的实例,是不是和__init__构造器类似,其实一个对象实例化的时候有2个过程,第一步是调用__new__函数返回实例,然后实例再去调用__init__函数(子类实例化的时候也是同样的过程,父类的__new__和__init__都会执行,一般都是子类实例去调用)。逐步分析
1 base = cls.configurable_base(),看看configurable_base(),没有找到(可以编译通过的),对,这是一个接口,既然找不到我们只能去实现类里面去找罗(难道这是接口不能实例化的原因吗?)去IOLoop找到了
@classmethod def configurable_base(cls): return IOLoop
返回的是一个IOLoop类所以base = IOLoop
2
if cls is base: impl = cls.configured_class() if base.__impl_kwargs: args.update(base.__impl_kwargs)
tornado.ioloop.IOLoop.instance()中instance()函数有实例化的动作IOLoop(),所以会调用接口的__new__函数,此时的cls就是IOloop了所以判断为true继续执行,我们可以在当前接口中扎到configure_class
@classmethod def configured_class(cls): """Returns the currently configured class.""" base = cls.configurable_base() if cls.__impl_class is None: base.__impl_class = cls.configurable_default() return base.__impl_class
它做什么的了,__impl_class在开头就定义了None我们将其理解为实现类所以到base.__impl_class = cls.configurable_default(),cls是什么前面说了IOLoop了,看看该方法
@classmethod def configurable_default(cls): if hasattr(select, "epoll"): from tornado.platform.epoll import EPollIOLoop return EPollIOLoop if hasattr(select, "kqueue"): # Python 2.6+ on BSD or Mac from tornado.platform.kqueue import KQueueIOLoop return KQueueIOLoop from tornado.platform.select import SelectIOLoop return SelectIOLoop
我们可以在IDLE中看看select模块看看有那些属性,我们发现这些属性都有(不知道windows下有没有,可以试试),"epoll"有的,执行第一个判断,成立,返回了EPollIOLoop类 impl = EPollIOLoop
3.
instance = super(Configurable, cls).__new__(impl)
看这一句,如果你要重写一个类的__new__这句话一定要加上并且return,不然不能实例化了。instance = super(Configurable, cls).__new__(EPollIOLoop)返回的是EPollIOLoop实例,约定俗成吧,
所以最后总结出来instance()函数中IOLoop()其实创建的是EPollIOLoop实例,它调用了父类的start()函数,我想为什么IOloop()生产的是这个实例,我仿照这个过程了写了例子
#父类,我们将其理解为Configurable接口 class Myc(object): def __init__(self): print "构造方" def __new__(cls): print "myc实例被创建" return super(Myc,cls).__new__(Myd) #默认创建Myd实例 def start(self): print "这是父类的start方法" #理解为PollIOLoop class Myd(Myc): @classmethod def print_cls(cls): print "子类的一个方法,创建的实例是",cls def retu_cls(self): return Myd #理解为EPollIOLoop继承了Myc class mye(Myc): def child(self): print "这是孙子mye" 测试一下e = mye() myc实例被创建 说明接口__new__被执行了但是__init__没有被执行,说明生成的不是Myc或子类的实例我们看看e可以调用那些方法,可不可以调用mye的child()函数了,发现不行,它可以调用Myd和Myc的函数,e.__class__()看看<__main__.Myd object at 0xb5ee0f6c>原来mye()创建的是Myd的实例