<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            8.5. localsglobals

            我們先偏離一下 HTML 處理的主題,討論一下 Python 如何處理變量。Python 有兩個內置的函數,localsglobals,它們提供了基于 dictionary 的訪問局部和全局變量的方式。

            還記得 locals 嗎?您第一次是在這里看到的:

                def unknown_starttag(self, tag, attrs):
                    strattrs = "".join([' %s="%s"' % (key, value) for key, value in attrs])
                    self.pieces.append("<%(tag)s%(strattrs)s>" % locals())
            

            不,等等,此時您還不能理解 locals 。首先,您需要學習關于命名空間的知識。這很枯燥,但是很重要,因此要要耐心些。

            Python 使用叫做名字空間的東西來記錄變量的軌跡。名字空間只是一個 dictionary ,它的鍵字就是變量名,它的值就是那些變量的值。實際上,名字空間可以像 Python 的 dictionary 一樣進行訪問,一會兒我們就會看到。

            在一個 Python 程序中的任何一個地方,都存在幾個可用的名字空間。每個函數都有著自已的名字空間,叫做局部名字空間,它記錄了函數的變量,包括函數的參數和局部定義的變量。每個模塊擁有它自已的名字空間,叫做全局名字空間,它記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。還有就是內置名字空間,任何模塊均可訪問它,它存放著內置的函數和異常。

            當一行代碼要使用變量 x 的值時,Python 會到所有可用的名字空間去查找變量,按照如下順序:

            1. 局部名字空間――特指當前函數或類的方法。如果函數定義了一個局部變量 x,或一個參數 xPython 將使用它,然后停止搜索。
            2. 全局名字空間――特指當前的模塊。如果模塊定義了一個名為 x 的變量,函數或類,Python 將使用它然后停止搜索。
            3. 內置名字空間――對每個模塊都是全局的。作為最后的嘗試,Python 將假設 x 是內置函數或變量。

            如果 Python 在這些名字空間找不到 x,它將放棄查找并引發一個 NameError 異常,同時傳遞 There is no variable named 'x' 這樣一條信息,回到 例 3.18 “引用未賦值的變量”,您會看到一路上都有這樣的信息。但是您并沒有體會到 Python 在給出這樣的錯誤之前做了多少的努力。

            重要
            Python 2.2 引入了一種略有不同但重要的改變,它會影響名字空間的搜索順序:嵌套的作用域。 在 Python 2.2 版本之前,當您在一個嵌套函數lambda 函數中引用一個變量時,Python 會在當前 (嵌套的或 lambda) 函數的名字空間中搜索,然后在模塊的名字空間。Python 2.2 將只在當前 (嵌套的或 lambda) 函數的名字空間中搜索,然后是在父函數的名字空間 中搜索,接著是模塊的名字空間中搜索。Python 2.1 可 以兩種方式工作,缺省地,按 Python 2.0 的方式工作。但是您可以把下面一行代碼增加到您的模塊頭部,使您的模塊工作起來像 Python 2.2 的方式:
            
            from __future__ import nested_scopes

            您是否為此而感到困惑?不要灰心!我敢說這一點非常酷。像 Python 中的許多事情一樣,名字空間在運行時直接可以訪問。怎么樣?不錯吧,局部名字空間可以通過內置的 locals 函數來訪問。全局 (模塊級別) 名字空間可以通過內置的 globals 函數來訪問。

            例 8.10. locals 介紹

            >>> def foo(arg): 1
            ...     x = 1
            ...     print locals()
            ...     
            >>> foo(7)        2
            {'arg': 7, 'x': 1}
            >>> foo('bar')    3
            {'arg': 'bar', 'x': 1}
            1 函數 foo 在它的局部名字空間中有兩個變量:arg (它的值是被傳入函數的) 和 x (它是在函數里定義的)。
            2 locals 返回一個名字/值對的 dictionary。這個 dictionary 的鍵字是字符串形式的變量名字,dictionary 的值是變量的實際值。所以用 7 來調用 foo,會打印出包含函數兩個局部變量的 dictionary:arg (7) 和 x (1)。
            3 回想一下,Python 有動態數據類型,所以您可以非常容易地傳遞給 arg 一個字符串,這個函數 (和對 locals 的調用) 將仍然很好的工作。locals 可以用于所有類型的變量。

            locals 對局部 (函數) 名字空間做了些什么,globals 就對全局 (模塊) 名字空間做了什么。然而 globals 更令人興奮,因為一個模塊的名字空間是更令人興奮的。[6] 模塊的名字空間不僅僅包含了模塊級的變量和常量,還包括了所有在模塊中定義的函數和類。除此以外,它還包括了任何被導入到模塊中的東西。

            回想一下 from module importimport module 之間的不同。使用 import module,模塊自身被導入,但是它保持著自已的名字空間,這就是為什么您需要使用模塊名來訪問它的函數或屬性:module.function 的原因。但是使用 from module import,實際上是從另一個模塊中將指定的函數和屬性導入到您自己的名字空間,這就是為什么您可以直接訪問它們卻不需要引用它們所來源的模塊。使用 globals 函數,您會真切地看到這一切的發生。

            例 8.11. globals 介紹

            看看下面列出的在文件 BaseHTMLProcessor.py 尾部的代碼塊:

            
            if __name__ == "__main__":
                for k, v in globals().items():             1
                    print k, "=", v
            1 不要被嚇壞了,想想以前您已經全部都看到過了。globals 函數返回一個 dictionary,我們使用 items 方法和多變量賦值遍歷 dictionary。在這里唯一的新東西就是 globals 函數。

            現在從命令行運行這個腳本,會得到下面的輸出 (注意您的輸出可能有略微的不同,這依賴于您的系統平臺和所安裝的 Python 版本):

            c:\docbook\dip\py> python BaseHTMLProcessor.py
            SGMLParser = sgmllib.SGMLParser                1
            htmlentitydefs = <module 'htmlentitydefs' from 'C:\Python23\lib\htmlentitydefs.py'> 2
            BaseHTMLProcessor = __main__.BaseHTMLProcessor 3
            __name__ = __main__                            4
            ... rest of output omitted for brevity...
            1 我們使用了 from module importSGMLParsersgmllib 中導入。也就是說它被直接導入到我們的模塊名字空間了,就是這樣。
            2 把上面的例子和 htmlentitydefs 對比一下,它是用 import 被導入的。也就是說 htmlentitydefs 模塊本身被導入了名字空間,但是定義在 htmlentitydefs 之中的 entitydefs 變量卻沒有。
            3 這個模塊只定義一個類,BaseHTMLProcessor,不錯。注意這兒的值就是類本身,不是一個特別的類實例。
            4 記得 if __name__ 技巧嗎?當運行一個模塊時 (相對于從另外一個模塊中導入而言),內置的 __name__ 是一個特殊值 __main__。因為我們是把這個模塊當作腳本從命令來運行的,故 __name__ 值為 __main__,這就是為什么我們這段簡單地打印 globals 的代碼可以執行的原因。
            注意
            使用 localsglobals 函數,通過提供變量的字符串名字您可以動態地得到任何變量的值。這種方法提供了這樣的功能:getattr 函數允許您通過提供函數的字符串名來動態地訪問任意的函數。

            localsglobals 之間有另外一個重要的區別,您應該在它困擾您之前就了解它。它無論如何都會困擾您的,但至少您還會記得曾經學習過它。

            例 8.12. locals 是只讀的,globals 不是

            
            def foo(arg):
                x = 1
                print locals()    1
                locals()["x"] = 2 2
                print "x=",x      3
            
            z = 7
            print "z=",z
            foo(3)
            globals()["z"] = 8    4
            print "z=",z          5
            
            1 因為使用 3 來調用 foo,會打印出 {'arg': 3, 'x': 1}。這個應該沒什么奇怪的。
            2 locals 是一個返回 dictionary 的函數,這里您在 dictionary 中設置了一個值。您可能認為這樣會改變局部變量 x 的值為 2,但并不會。locals 實際上沒有返回局部名字空間,它返回的是一個拷貝。所以對它進行改變對局部名字空間中的變量值并無影響。
            3 這樣會打印出 x= 1,而不是 x= 2
            4 在有了對 locals 的經驗之后,您可能認為這樣不會 改變 z 的值,但是可以。由于 Python 在實現過程中內部有所區別 (關于這些區別我寧可不去研究,因為我自已還沒有完全理解) ,globals 返回實際的全局名字空間,而不是一個拷貝:與 locals 的行為完全相反。所以對 globals 所返回的 dictionary 的任何的改動都會直接影響到全局變量。
            5 這樣會打印出 z= 8,而不是 z= 7

            Footnotes

            [6] 我沒有說得太多吧。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线