Twisted中的putChild和getChild
? ? ? ? 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 Sitefrom calendar import calendarclass YearPage(Resource):def __init__(self, year):Resource.__init__(self)self.year = yeardef 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 selfif 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 Sitefrom calendar import calendarclass HelpPage(Resource):isLeaf = Truedef render_GET(self, request):return "<html><body>help</body></html>"class YearPage(Resource):def __init__(self, year):Resource.__init__(self)self.year = yearself.putChild("help", HelpPage())def render_GET(self, request):return "<html><body><pre>%s</pre></body></html>" % (calendar(self.year), )class RoomPage(Resource):isLeaf = Truedef 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 selfreturn 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 selfreturn 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。
總結
以上是生活随笔為你收集整理的Twisted中的putChild和getChild的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python2和Python3中@abs
- 下一篇: twisted系列教程十九–cancel