您的位置:首页 > 编程语言 > Python开发

Twisted中的putChild和getChild

2016-03-05 21:03 441 查看
    Twisted的官方文档上对于putChild的解释是“Register a static child”(点击打开链接),即为当前资源节点注册一个静态子资源节点。实际上,Resource类中的putChild实现的是IResource接口中的putChild方法(点击打开链接)。

    Resource类中还有一个getChild方法,官方文档的解释是“Retrieve a 'child' resource from me. Implement this to create dynamic resource generation -- resources which are always available may be registered with self.putChild()”(点击打开链接)。这两句话说明getChild的作用是从当前资源节点获取子资源节点。但是,与putChild不同,getChild方法能够创建动态资源。

    理解putChild和getChild的关键在于理解“静态资源”和“动态资源”两个概念。下面先看一个书上(Twisted Network Programming Essentials)的例子:

from twisted.internet import reactor
from twisted.web.resource import Resource, NoResource
from twisted.web.server import Site

from calendar import calendar

class YearPage(Resource):
def __init__(self, year):
Resource.__init__(self)
self.year = year

def render_GET(self, request):
return "<html><body><pre>%s</pre></body></html>" % (calendar(self.year), )

class CalendarHome(Resource):
def getChild(self, name, request):
if name == "":
return self
if name.isdigit():
return YearPage(int(name))
else:
return NoResource()

def render_GET(self, request):
return "<html><body>Welcome to the calendar server!</body></html>"

root = CalendarHome()
factory = Site(root)
reactor.listenTCP(8000, factory)
reactor.run()

假设上面代码在本地运行,如果我们访问localhost:8000,CalendarHome的实例root的getChild会被调用,此时传给getChild的name实参为“”,getChild返回实例自身(self);同时,该实例调用render_GET函数渲染自己。如果访问localhost:8000/2016,此时传给getChild的name实参为"2016",这时,getChild负责新建一个YearPage实例,该实例调用render_GET函数渲染自己。如果在浏览器中打开localhost:8000/2016页面,每次刷新都将调用root的getChild函数,而每次调用都会创建一个新的YearPage实例。因此,那些像YearPage一样、访问时才被创建的资源,就是“动态资源”。

    现在把上面的代码稍加改写,如下所示:

from twisted.internet import reactor
from twisted.web.resource import Resource, NoResource
from twisted.web.server import Site

from calendar import calendar

class HelpPage(Resource):
isLeaf = True

def render_GET(self, request):
return "<html><body>help</body></html>"

class YearPage(Resource):
def __init__(self, year):
Resource.__init__(self)
self.year = year
self.putChild("help", HelpPage())

def render_GET(self, request):
return "<html><body><pre>%s</pre></body></html>" % (calendar(self.year), )

class RoomPage(Resource):
isLeaf = True

def render_GET(self, request):
return "<html><body>room</body></html>"

class HomePage(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("room", RoomPage())

def render_GET(self, request):
return "<html><body>home</body></html>"

class CalendarHome(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("home", HomePage())
self.putChild("year", YearPage(2016))

def getChild(self, name, request):
if name == "":
return self
return NoResource()

def render_GET(self, request):
return "<html><body>Welcome to the calendar server!</body></html>"

root = CalendarHome()
factory = Site(root)
reactor.listenTCP(8000, factory)
reactor.run()

与上一段代码不同,这段代码在CalendarHome实例构造阶段就用putChild函数为该实例添加了两个子资源节点:home对应一个HomePage实例,“year”对应一个YearPage实例。如果用浏览器打开localhost:8000/year,无论反复刷新多少次,得到的都是来自同一个YearPage实例的响应。同理,用浏览器分别打开localhost:8000/year/help,localhost:8000/home和localhost:8000/home/room,反复刷新,相应的实例不会反复重新生成,响应浏览器GET请求的都是相应的父资源节点调用putChild函数时注册生成的那一个。因此,这些实例一旦被创建就会驻留在内存中,被反复使用。这就是“静态资源”的含义。

    再对上面的代码中CalendarHome的构造函数稍作修改:

class CalendarHome(Resource):
def __init__(self):
Resource.__init__(self)
self.putChild("", HomePage())
self.putChild("year", YearPage(2016))

def getChild(self, name, request):
if name == "":
return self
return NoResource()

def render_GET(self, request):
return "<html><body>Welcome to the calendar server!</body></html>"

如上面代码所示,将HomePage实例注册在CalendarHome“根目录”下。这时就会出现一个问题:getChild函数要求当name为“”,即访问localhost:8000时,返回CalendarHome实例自身,然后调用自身的render_GET函数渲染;而putChild函数似乎要求访问localhost:8000时返回一个HomePage实例。到底听谁的?实验证明,听putChild的,即访问localhost:8000时,将调用HomePage实例的render_GET函数进行渲染。事实上,只有当某个资源节点没有被putChild函数注册生成时,getChild函数才会被调用,用于查找并返回相应的资源。

   需要注意的是,此时访问localhost:8000/room会出现404错误。实际上,这是CalendarHome的getChild函数在作怪。因为getChild函数中写得清清楚楚,当name不为“”时,一律返回NoResource。如果把getChild代码删掉,再访问localhost:8000/room呢?答案依然是404。这是因为CalendarHome的getChild方法继承自Resource类,而Resource类中的getChild源码如下:

161	    def getChild(self, path, request):
162	        """
163	        Retrieve a 'child' resource from me.
164
165	        Implement this to create dynamic resource generation -- resources which
166	        are always available may be registered with self.putChild().
167
168	        This will not be called if the class-level variable 'isLeaf' is set in
169	        your subclass; instead, the 'postpath' attribute of the request will be
170	        left as a list of the remaining path elements.
171
172	        For example, the URL /foo/bar/baz will normally be::
173
174	          | site.resource.getChild('foo').getChild('bar').getChild('baz').
175
176	        However, if the resource returned by 'bar' has isLeaf set to true, then
177	        the getChild call will never be made on it.
178
179	        Parameters and return value have the same meaning and requirements as
180	        those defined by L{IResource.getChildWithDefault}.
181	        """
182	        return NoResource("No such child resource.")

可见,如果CalendarHome不重写父类中的getChild方法,一旦CalendarHome的getChild被调用,直接返回NoResource。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息