Twisted中的putChild和getChild

1 篇文章 0 订阅
1 篇文章 0 订阅

    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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值