posts - 403, comments - 310, trackbacks - 0, articles - 7
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          [zz]True closure in Python

          Posted on 2008-02-11 16:18 ZelluX 閱讀(817) 評(píng)論(0)  編輯  收藏 所屬分類: Scripting
          發(fā)現(xiàn)自己對(duì)Python的語(yǔ)法的興趣遠(yuǎn)比對(duì)使用Python本身的興趣濃厚得多
          為什么水木上的帖子每行末尾都是用空格填充的,每次轉(zhuǎn)載還要先放到vim里面處理一下。。。

          by ilovecpp

          讓Python支持true closure有多難?
          只需修改11行代碼。

          如果你不知道什么是true closure,這里簡(jiǎn)單解釋一下。Python支持lexicalscope:

          >>> def add_n(n):
          ...     def f(m):
          ...             return n+m
          ...     return f
          >>> add_2 = add_n(2)
          >>> add_2(0)
          2
          >>> add_2(2)
          4

          f引用了外層函數(shù)add_n的局部變量n。有趣的是,f引用n的時(shí)候,add_n已經(jīng)結(jié)束,n似乎不存在了。f所以能正常工作,是因?yàn)閯?chuàng)建它的時(shí)候就把n作為f的上下文(closure)保存了下來(lái),并不隨add_n結(jié)束而消失。
          但是,Python的lexical scope和Scheme/Smalltalk/Ruby還有一點(diǎn)區(qū)別:不能在內(nèi)層函數(shù)中rebind外層函數(shù)的局部變量。
          >>> def f():
          ...     def g():
          ...             n=1
          ...     n=0
          ...     g()
          ...     return n
          ...
          >>> f()
          0

          這是因?yàn)镻ython沒(méi)有變量聲明, n=1 自動(dòng)使n成為g的局部變量,也就無(wú)法rebind f中的n了。可以說(shuō)Python的closure是只讀的。如果你聽(tīng)到有人說(shuō)"Python不支持true closure",就是指這個(gè)。其實(shí),Python VM能夠支持true closure。因?yàn)椋琍ython支持內(nèi)層函數(shù)看見(jiàn)外層函數(shù)的name rebinding:

          >>> def f():
          ...     def g():
          ...             yield n
          ...             yield n
          ...     x = g()
          ...     n = 0
          ...     print x.next()
          ...     n = 1
          ...     print x.next()
          ...
          >>> f()
          0
          1

          對(duì)于Python的closure實(shí)現(xiàn)(flat closure),"外層函數(shù)rebind name"和"內(nèi)層函數(shù)rebind name"其實(shí)沒(méi)有區(qū)別。我們知道用global關(guān)鍵字可以rebind module scopename。如果增加一個(gè)類似的outer關(guān)鍵字,就可以支持rebind outer scope name。真正的限制是Guido不愿意為支持true closure增加關(guān)鍵字。

          也可以不增加關(guān)鍵字,而是把global n的語(yǔ)義改為"如果outer scope定義了n,rebind outer scope n;否則rebind module scope n"。簡(jiǎn)單起見(jiàn),我沒(méi)有修改Python的built-in compiler,而是修改了compiler module(用Python實(shí)現(xiàn)的Python compiler)。你只需把下面這個(gè)patch打到compiler/symbols.py(Python 2.5.1)就可以體驗(yàn)true closure了:

          C:\Python\Lib>diff -u compiler/symbols.py.orig compiler/symbols.py
          --- compiler/symbols.py.orig    Thu Aug 17 10:28:56 2006
          +++ compiler/symbols.py Mon Feb 11 12:03:01 2008
          @@ -21,6 +21,7 @@
                   self.params = {}
                   self.frees = {}
                   self.cells = {}
          +        self.outers = {}
                   self.children = []
                   # nested is true if the class could contain free variables,
                   # i.e. if it is nested within another function.
          @@ -54,8 +55,10 @@
                   if self.params.has_key(name):
                       raise SyntaxError, "%s in %s is global and parameter" % \
                             (name, self.name)
          -        self.globals[name] = 1
          -        self.module.add_def(name)
          +        if self.nested:
          +            self.outers[name] = 1
          +        else:
          +            self.globals[name] = 1

               def add_param(self, name):
                   name = self.mangle(name)
          @@ -90,6 +93,8 @@
                   """
                   if self.globals.has_key(name):
                       return SC_GLOBAL
          +        if self.outers.has_key(name):
          +            return SC_FREE
                   if self.cells.has_key(name):
                       return SC_CELL
                   if self.defs.has_key(name):
          @@ -107,6 +112,7 @@
                       return ()
                   free = {}
                   free.update(self.frees)
          +        free.update(self.outers)
                   for name in self.uses.keys():
                       if not (self.defs.has_key(name) or
                               self.globals.has_key(name)):
          @@ -134,6 +140,9 @@
                   free.
                   """
                   self.globals[name] = 1
          +        if self.outers.has_key(name):
          +            self.module.add_def(name)
          +            del self.outers[name]
                   if self.frees.has_key(name):
                       del self.frees[name]
                   for child in self.children:

          因?yàn)槲覀儧](méi)有修改built-in compiler,所以程序要寫在字符串里,用compiler.compile編譯,用exec執(zhí)行:
          >>> from compiler import compile
          >>> s = '''
          ... def counter():
          ...     n = 0
          ...     def inc():
          ...             global n
          ...             n += 1
          ...     def dec():
          ...             global n
          ...             n -= 1
          ...     def get():
          ...             return n
          ...     return inc, dec, get
          ... '''
          >>> exec compile(s, '', 'exec')
          >>> inc, dec, get = counter()
          >>> get()
          0
          >>> inc()
          >>> get()
          1
          >>> dec()
          >>> get()
          0

          后記

          1 搞這個(gè)東西的緣起是Selfless Python(http://www.voidspace.org.uk/python/weblog/arch_d7_2006_12_16.shtml#e583)。很有趣的bytecode hack,給一個(gè)類中的所有函數(shù)補(bǔ)上self參數(shù)。既然PythonVM支持true closure,能不能用類似的手法讓Python支持true closure呢?不過(guò)很快就明白這個(gè)在bytecode層面不好弄,還是得修改編譯器。不過(guò)改起來(lái)還真是出乎意料地簡(jiǎn)單。

          2 Guido早已明確表示不能改變global的語(yǔ)義(因?yàn)闀?huì)影響現(xiàn)有代碼),所以這個(gè)只是玩玩而已,不用指望成為現(xiàn)實(shí)。當(dāng)然你可以只發(fā)布bytecode,大概還能把反編譯器搞掛掉。:-)
          3 我可以理解Guido的決定。除非你之前一直在用Scheme,否則我覺(jué)得像上面counter例子那種一組共享狀態(tài)的函數(shù)還是寫成class為好,至少共享狀態(tài)是什么一目了然。Lexical scope太implicit,用在開(kāi)頭add_n那種地方挺方便,再?gòu)?fù)雜就不好了。

          又:很抱歉"幕后的故事"拖了這么久。寫起來(lái)才發(fā)現(xiàn)自己還是不懂descriptor。
          不過(guò)我肯定不會(huì)讓它爛尾的。

           

          主站蜘蛛池模板: 如皋市| 葵青区| 巨鹿县| 西畴县| 广州市| 安仁县| 灵璧县| 蓬安县| 塔城市| 太仆寺旗| 乐业县| 眉山市| 云南省| 贡觉县| 思茅市| 平江县| 璧山县| 分宜县| 巴南区| 诸城市| 同江市| 内丘县| 丽江市| 光泽县| 和硕县| 云龙县| 临高县| 平陆县| 九寨沟县| 桐乡市| 资源县| 东城区| 兰考县| 独山县| 丰台区| 深泽县| 凤台县| 武夷山市| 云龙县| 焉耆| 正镶白旗|