每过一阵子总有那么几天,想要手残一下。这不又格式化了整个硬盘,重装了双系统。结果 Win10+A卡 玩不了撸啊撸了…… 只好老老实实回到 Ubuntu,在网上乱入发现了这张图,给我的逃课行为提供了有力的支撑~
然后,这个基于git-pages
的 Blog 也随之诞生了。
扯谈的分割区
不知天高地厚的看了一下Tornado
的ioloop
模块的源码,结果是毫无头绪,其它的就暂且不说。tornado.ioloop.IOLoop.instance().start()
是用来启动Tornado
的,但是在源码里面IOLoop().start()
压根就没实现,子类PollIOLoop().start()
倒是实现了。看看instance()
到底是啥:
1
2
3
>>> import tornado.ioloop
>>> repr(tornado.ioloop.IOLoop.instance())
'<tornado.platform.epoll.EPollIOLoop object at 0x7f4ac177ae50>'
哪里又冒出了一个EPollIOLoop
?
util.Configurable
的文档是这样描述的:Base class for configurable interfaces.这是个抽象类,它的构造函数(__new__()
[1])给其实现子类之一(应该就是这厮了ioloop.IOLoop
)扮演工厂函数的角色。还说了其实现子类可以用configure()
在运行时全局的改变实例,不过这里貌似没用到。通过用构造方法作为工厂方法,这个接口就像正常的类一样,isinstance
之类的方法都可以正常使用。这种模式在这些时候是最有用的:当选择的实现可能是一个全局的决定时(如果epoll
可用就会代替select
,在*unix上都可用),或者previously-monolithic class
(what?)被分为特定的子类时。
看了类文档,眼前一亮,难道这就是我们上节设计模式课所说的工厂方法模式,不是也是了,反正上课也没听太懂……
这几个类的作用如下:
util.Configurable
:通过构造函数__new__()
作为工厂方法来创建实例。ioloop.IOLoop
:当你调用tornado.ioloop.IOLoop.instance()
时,会根据你所使用的平台通过configrable_defult()
来返回SlectIOLoop
/EpollIOLoop
/KQueueIOLoop
中最佳的一个类给util.Configurable
来创建实例。ioloop.PollIOLoop
:为IOLoop
来创建接口一致的select-like
方法。platform.select.SlectIOLoop
/platform.epoll.EpollIOLoop
/platform.kqueue.KQueueIOLoop
:与平台相关的特定子类。
看看这个简化版的代码片段,了解一下这个魔法是如何进行的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
from __future__ import print_function
EPOLL, SELECT = 2, 1
class Configurable(object):
def __new__(cls, **kwargs):
""" 构造器, 工厂方法 """
impl = cls.configurable_default()
instance = super(Configurable, cls).__new__(impl)
instance.initialize(**kwargs)
return instance
@classmethod
def configurable_default(cls):
raise NotImplementedError()
def initialize(self): # 都可以替换成 __init__,因为__init__ 并不是构造函数
pass
class IOLoop(Configurable):
@staticmethod
def instance():
""" 简单的单例模式 """
if not hasattr(IOLoop, '_instance'):
IOLoop._instance = IOLoop()
print("IOLoop -> instance()")
return IOLoop._instance
@classmethod
def configurable_default(cls):
print("IOLoop -> configurable_default()")
if EPOLL: # 假设 EPOLL是最佳方式
return EPollIOLoop
return SelectIOLoop
def initialize(self):
print("IOLoop -> initialize()")
pass
class PollIOLoop(IOLoop):
def initialize(self, impl, **kwargs):
print("PollIOLoop -> initialize()")
super(PollIOLoop, self).initialize()
class EPollIOLoop(PollIOLoop):
def initialize(self, **kwargs):
print("EPollIOLoop -> initialize()")
super(EPollIOLoop, self).initialize(impl=EPOLL, **kwargs)
class SelectIOLoop(PollIOLoop):
def initialize(self, **kwargs):
print("SelectIOLoop -> initialize()")
super(SelectIOLoop, self).initialize(impl=SELECT, **kwargs)
if __name__ == '__main__':
print(repr(IOLoop.instance()))
print(repr(IOLoop.instance()))
输出
1
2
3
4
5
6
7
8
IOLoop -> configurable_default()
EPollIOLoop -> initialize()
PollIOLoop -> initialize()
IOLoop -> initialize()
IOLoop -> instance()
<__main__.EPollIOLoop object at 0x7f91f7da1590> # 两个对象id一致
IOLoop -> instance()
<__main__.EPollIOLoop object at 0x7f91f7da1590>
在IOLoop.instance()
里调用IOLoop()
时,会调用Configurable
的__new__()
来实例化一个对象,调用__new__
的时候会调用IOLoop.configurable_default()
来获取一个最佳的选择(EPollIOLoop
),然后实例化这个选择,返回这个实例,所以IOLoop()
得到了实例是一个EPollIOLoop
对象。
可见工厂方法模式的核心就是如何去实例化并得到一个
具体的实例对象,但是系统并不希望我们的代码与该类的子类形成耦合或者我们根本不知道该类有哪些子类可用或者是我们不知道用哪个子类更佳。Tornado
这里就用的很好,让IOLoop
根据平台去决定实例化那个子类,而不要用户操心子类是如何创建的,只需要知道IOLoop
有哪些方法即可。
BTW: Tornado
的源码远比我想象的要复杂,简单的看了一下httpserver
的结构岂止于
错综复杂,看来想弄懂app = Application(); http_server = tornado.httpserver.HTTPServer(app); http_server.listen(options.port); tornado.ioloop.IOLoop.instance().start()
这几句简单的代码都不容易,but sooner and later ……