(代码缩进有点问题 大家可以看源码)
我们来分析一下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
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
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的实例
网友评论