1. 程式人生 > >Python標準庫(各種模組介紹)

Python標準庫(各種模組介紹)

"We'd like to pretend that 'Fredrik' is a role, but even hundreds of volunteers couldn't possibly keep up. No, 'Fredrik' is the result of crossing an http server with a spam filter with an emacs whatsit and some other stuff besides."
-Gordon McMillan, June 1998

Python 2.0釋出附帶了一個包含200個以上模組的可擴充套件的標準庫. 本書簡要地介紹每個模組並提供至少一個例子來說明如何使用它. 本書一共包含360個例子.

0.1. 關於本書

"Those people who have nothing better to do than post on the Internet all day long are rarely the ones who have the most insights."
- Jakob Nielsen, December 1998

五年前我偶然遇到了 Python, 開始了我的 Python 之旅, 我花費了大量的時間 在 comp.lang.python 新聞組裡回答問題. 也許某個人發現一個模組正是他想要的, 但是卻不知道如何使用它. 也許某個人為他的任務挑選的不合適的模組. 也許某個人已經厭 倦了發明新輪子. 大多時候, 一個簡短的例子要比一份手冊文件更有幫助.

本書是超過3,000個新聞組討論的精華部分, 當然也有很多的新指令碼, 為了涵蓋標準庫的每個角落.

我盡力使得每個指令碼都易於理解, 易於重用程式碼. 我有意縮短註釋的長度, 如果你想更深入地 瞭解背景, 那麼你可以參閱每個 Python 釋出中的參考手冊. 本書的重要之處在於範例程式碼.

我們歡迎任何評論, 建議, 以及 bug 報告, 請將它們傳送到 [email protected]. 我將閱讀盡我所能閱讀所有的郵件, 但可能回覆不是那麼及時.

為什麼沒有Tkinter?

本書涵蓋了整個標準庫, 除了(可選的)Tkinter ui(user-interface : 使用者介面) 庫. 有很多原因, 更多是因為時間, 本書的空間, 以及我正在寫另一本關於 Tkinter 的書.

產品細節

本書使用DocBook SGML編寫, 我使用了一系列的工具, 包括Secret Labs' PythonWorks, Excosoft Documentor, James Clark's Jade DSSSL processor, Norm Walsh's DocBook stylesheets, 當然,還有一些 Python 指令碼.

感謝幫忙校對的人們: Tim Peters, Guido van Rossum, David Ascher, Mark Lutz, 和 Rael Dornfest, 以及 PythonWare 成員: Matthew Ellis, Håkan Karlsson, 和 Rune Uhlin.

感謝 Lenny Muellner, 他幫助我把SGML檔案轉變為你們現在所看到的這本書, 以及Christien Shangraw, 他將那些程式碼檔案集合起來做成了隨書CD (可以在http://examples.oreilly.com/pythonsl 找到, 竟然沒有404, 奇蹟).

0.2. 程式碼約定

本書使用以下習慣用法:

斜體

用於檔名和命令. 還用於定義術語.

等寬字型 e.g. Python

用於程式碼以及方法,模組,操作符,函式,語句,屬性等的名稱.

等寬粗體

用於程式碼執行結果.

0.3. 關於例子

除非提到,所有例子都可以在 Python 1.5.2 和 Python 2.0 下執行. 能不能在 Python 2.4/2.5 下執行.....看參與翻譯各位的了.

除了一些平臺相關模組的指令碼, 所有例子都可以在 Windows, Solaris, 以及 Linux 下正常執行.

所有程式碼都是有版權的. 當然,你可以自由地使用這些這些模組,別忘記你是從哪得到(?學會)這些的.

大多例子的檔名都包含它所使用的模組名稱,後邊是 "-example-" 以及一個唯一的"序號". 注意有些例子並不是按順序出現的, 這是為了匹配本書的較早版本 -(the eff-bot guide to) The Standard Python Library.

0.4. 如何聯絡我們

Python 江湖 QQ 群: 43680167

Feather (校對) QQ: 85660100

1. 核心模組

"Since the functions in the C runtime library are not part of the Win32 API, we believe the number of applications that will be affected by this bug to be very limited."
- Microsoft, January 1999

1.1. 介紹

Python 的標準庫包括了很多的模組, 從 Python 語言自身特定的型別和宣告, 到一些只用於少數程式的不著名的模組.

本章描述了一些基本的標準庫模組. 任何大型 Python 程式都有可能直接或間接地使用到這類模組的大部分.

1.1.1. 內建函式和異常

下面的這兩個模組比其他模組加在一起還要重要: 定義內建函式(例如 len, int, range ...)的 _ _builtin_ _ 模組, 以及定義所有內建異常的 exceptions 模組.

Python 在啟動時匯入這兩個模組, 使任何程式都能夠使用它們.

1.1.2. 作業系統介面模組

Python 有許多使用了 POSIX 標準 API 和標準 C 語言庫的模組. 它們為底層作業系統提供了平臺獨立的介面.

這類的模組包括: 提供檔案和程序處理功能的 os 模組; 提供平臺獨立的檔名處理 (分拆目錄名, 檔名, 字尾等)的 os.path 模組; 以及時間日期處理相關的time/datetime 模組.

[!Feather注: datetime 為 Py2.3 新增模組, 提供增強的時間處理方法 ]

延伸一點說, 網路和執行緒模組同樣也可以歸為這一個型別. 不過 Python 並沒有在所有的平臺/版本實現這些.

1.1.3. 型別支援模組

標準庫裡有許多用於支援內建型別操作的庫. string 模組實現了常用的字串處理. math 模組提供了數學計算操作和常量(pi, e都屬於這類常量), cmath 模組為複數提供了和 math 一樣的功能.

1.1.4. 正則表示式

re 模組為 Python 提供了正則表示式支援. 正則表示式是用於匹配字串或特定子字串的 有特定語法的字串模式.

1.1.5. 語言支援模組

sys 模組可以讓你訪問直譯器相關引數,比如模組搜尋路徑,直譯器版本號等. operator 模組提供了和內建操作符作用相同的函式. copy 模組允許 你複製物件, Python 2.0 新加入的 gc 模組提供了對垃圾收集的相關控制功能.

1.2. _ _builtin_ _ 模組

這個模組包含 Python 中使用的內建函式. 一般不用手動匯入這個模組; Python會幫你做好一切.

1.2.1. 使用元組或字典中的引數呼叫函式

Python允許你實時地建立函式引數列表. 只要把所有的引數放入一個元組中, 然後通過內建的 apply 函式呼叫函式. 如 Example 1-1.

1.2.1.1. Example 1-1. 使用 apply 函式

File: builtin-apply-example-1.py

def function(a, b):
    print a, b

apply(function, ("whither", "canada?"))
apply(function, (1, 2 + 3))

whither canada?
1 5

要想把關鍵字引數傳遞給一個函式, 你可以將一個字典作為 apply 函式的第 3 個引數, 參考 Example 1-2.

1.2.1.2. Example 1-2. 使用 apply 函式傳遞關鍵字引數

File: builtin-apply-example-2.py

def function(a, b):
    print a, b

apply(function, ("crunchy", "frog"))
apply(function, ("crunchy",), {"b": "frog"})
apply(function, (), {"a": "crunchy", "b": "frog"})

crunchy frog
crunchy frog
crunchy frog

apply 函式的一個常見用法是把建構函式引數從子類傳遞到基類, 尤其是建構函式需要接受很多引數的時候. 如 Example 1-3 所示.

1.2.1.3. Example 1-3. 使用 apply 函式呼叫基類的建構函式

File: builtin-apply-example-3.py

class Rectangle:
    def _ _init_ _(self, color="white", width=10, height=10):
        print "create a", color, self, "sized", width, "x", height

class RoundedRectangle(Rectangle):
    def _ _init_ _(self, **kw):
        apply(Rectangle._ _init_ _, (self,), kw)

rect = Rectangle(color="green", height=100, width=100)
rect = RoundedRectangle(color="blue", height=20)

create a green <Rectangle instance at 8c8260> sized 100 x 100
create a blue <RoundedRectangle instance at 8c84c0> sized 10 x 20

Python 2.0 提供了另個方法來做相同的事. 你只需要使用一個傳統的函式呼叫 , 使用 * 來標記元組, ** 來標記字典.

下面兩個語句是等價的:

result = function(*args, **kwargs)
result = apply(function, args, kwargs)

1.2.2. 載入和過載模組

如果你寫過較龐大的 Python 程式, 那麼你就應該知道 import 語句是用來匯入外部模組的 (當然也可以使用 from-import 版本). 不過你可能不知道 import 其實是靠呼叫內建 函式 _ _import_ _ 來工作的.

通過這個戲法你可以動態地呼叫函式. 當你只知道模組名稱(字串)的時候, 這將很方便. Example 1-4 展示了這種用法, 動態地匯入所有以 "-plugin" 結尾的模組.

1.2.2.1. Example 1-4. 使用 _ _import_ _ 函式載入模組

File: builtin-import-example-1.py

import glob, os

modules = []

for module_file in glob.glob("*-plugin.py"):
    try:
        module_name, ext = os.path.splitext(os.path.basename(module_file))
        module = _ _import_ _(module_name)
        modules.append(module)
    except ImportError:
        pass # ignore broken modules

# say hello to all modules
for module in modules:
    module.hello()

example-plugin says hello

注意這個 plug-in 模組檔名中有個 "-" (hyphens). 這意味著你不能使用普通的 import 命令, 因為 Python 的辨識符不允許有 "-" .

1.2.2.2. Example 1-5. Plug-in 例子

File: example-plugin.py

def hello():
    print "example-plugin says hello"

Example 1-6 展示瞭如何根據給定模組名和函式名獲得想要的函式物件.

1.2.2.3. Example 1-6. 使用 _ _import_ _ 函式獲得特定函式

File: builtin-import-example-2.py

def getfunctionbyname(module_name, function_name):
    module = _ _import_ _(module_name)
    return getattr(module, function_name)

print repr(getfunctionbyname("dumbdbm", "open"))

<function open at 794fa0>

你也可以使用這個函式實現延遲化的模組匯入 (lazy module loading). 例如在 Example 1-7 中 的 string 模組只在第一次使用的時候匯入.

1.2.2.4. Example 1-7. 使用 _ _import_ _ 函式實現 延遲匯入

File: builtin-import-example-3.py

class LazyImport:
    def _ _init_ _(self, module_name):
        self.module_name = module_name
        self.module = None
    def _ _getattr_ _(self, name):
        if self.module is None:
            self.module = _ _import_ _(self.module_name)
        return getattr(self.module, name)

string = LazyImport("string")

print string.lowercase

abcdefghijklmnopqrstuvwxyz

Python 也提供了重新載入已載入模組的基本支援. [Example 1-8 #eg-1-8 會載入 3 次 hello.py 檔案.

1.2.2.5. Example 1-8. 使用 reload 函式

File: builtin-reload-example-1.py

import hello
reload(hello)
reload(hello)

hello again, and welcome to the show
hello again, and welcome to the show
hello again, and welcome to the show

reload 直接接受模組作為引數.

[!Feather 注:  ^ 原句無法理解, 稍後討論.]

注意,當你重載入模組時, 它會被重新編譯, 新的模組會代替模組字典裡的老模組. 但是, 已經用原模組裡的類建立的例項仍然使用的是老模組(不會被更新).

同樣地, 使用 from-import 直接建立的到模組內容的引用也是不會被更新的.

1.2.3. 關於名稱空間

dir 返回由給定模組, 類, 例項, 或其他型別的所有成員組成的列表. 這可能在互動式 Python 直譯器下很有用, 也可以用在其他地方. Example 1-9展示了 dir 函式的用法.

1.2.3.1. Example 1-9. 使用 dir 函式

File: builtin-dir-example-1.py

def dump(value):
    print value, "=>", dir(value)

import sys

dump(0)
dump(1.0)
dump(0.0j) # complex number
dump([]) # list
dump({}) # dictionary
dump("string")
dump(len) # function
dump(sys) # module

0 => []
1.0 => []
0j => ['conjugate', 'imag', 'real']
[] => ['append', 'count', 'extend', 'index', 'insert',
    'pop', 'remove', 'reverse', 'sort']
{} => ['clear', 'copy', 'get', 'has_key', 'items',
    'keys', 'update', 'values']
string => []
<built-in function len> => ['_ _doc_ _', '_ _name_ _', '_ _self_ _']
<module 'sys' (built-in)> => ['_ _doc_ _', '_ _name_ _',
    '_ _stderr_ _', '_ _stdin_ _', '_ _stdout_ _', 'argv',
    'builtin_module_names', 'copyright', 'dllhandle',
    'exc_info', 'exc_type', 'exec_prefix', 'executable',
...

在例子 Example 1-10中定義的 getmember 函式返回給定類定義的所有類級別的屬性和方法.

1.2.3.2. Example 1-10. 使用 dir 函式查詢類的所有成員

File: builtin-dir-example-2.py

class A:
    def a(self):
        pass
    def b(self):
        pass

class B(A):
    def c(self):
        pass
    def d(self):
        pass

def getmembers(klass, members=None):
    # get a list of all class members, ordered by class
    if members is None:
        members = []
    for k in klass._ _bases_ _:
        getmembers(k, members)
    for m in dir(klass):
        if m not in members:
            members.append(m)
    return members

print getmembers(A)
print getmembers(B)
print getmembers(IOError)

['_ _doc_ _', '_ _module_ _', 'a', 'b']
['_ _doc_ _', '_ _module_ _', 'a', 'b', 'c', 'd']
['_ _doc_ _', '_ _getitem_ _', '_ _init_ _', '_ _module_ _', '_ _str_ _']

getmembers 函式返回了一個有序列表. 成員在列表中名稱出現的越早, 它所處的類層次就越高. 如果無所謂順序的話, 你可以使用字典代替列表.

[!Feather 注: 字典是無序的, 而列表和元組是有序的, 網上有關於有序字典的討論]

vars 函式與此相似, 它返回的是包含每個成員當前值的字典. 如果你使用不帶引數的 vars , 它將返回當前區域性名稱空間的可見元素(同 locals() 函式 ). 如Example 1-11所表示.

1.2.3.3. Example 1-11. 使用 vars 函式

File: builtin-vars-example-1.py

book = "library2"
pages = 250
scripts = 350


print "the %(book)s book contains more than %(scripts)s scripts" % vars()

the library book contains more than 350 scripts

1.2.4. 檢查物件型別

Python 是一種動態型別語言, 這意味著給一個定變數名可以在不同的場合繫結到不同的型別上. 在接下面例子中, 同樣的函式分別被整數, 浮點數, 以及一個字串呼叫:

def function(value):
    print value
function(1)
function(1.0)
function("one")

type 函式 (如 Example 1-12 所示) 允許你檢查一個變數的型別. 這個函式會返回一個 type descriptor (型別描述符), 它對於 Python 直譯器提供的每個型別都是不同的.

1.2.4.1. Example 1-12. 使用 type 函式

File: builtin-type-example-1.py

def dump(value):
    print type(value), value

dump(1)
dump(1.0)
dump("one")

<type 'int'> 1
<type 'float'> 1.0
<type 'string'> one

每個型別都有一個對應的型別物件, 所以你可以使用 is 操作符 (物件身份?) 來 檢查型別. (如 Example 1-13所示).

1.2.4.2. Example 1-13. 對檔名和檔案物件使用 type 函式

File: builtin-type-example-2.py

def load(file):
    if isinstance(file, type("")):
        file = open(file, "rb")
    return file.read()

print len(load("samples/sample.jpg")), "bytes"
print len(load(open("samples/sample.jpg", "rb"))), "bytes"


4672 bytes
4672 bytes

callable 函式, 如 Example 1-14 所示, 可以檢查一個物件是否是可呼叫的 (無論是直接呼叫或是通過 apply). 對於函式, 方法, lambda 函式, 類, 以及實現了 _ _call_ _ 方法的類例項, 它都返回 True.

1.2.4.3. Example 1-14. 使用 callable 函式

File: builtin-callable-example-1.py

def dump(function):
    if callable(function):
        print function, "is callable"
    else:
        print function, "is *not* callable"

class A:
    def method(self, value):
        return value

class B(A):
    def _ _call_ _(self, value):
        return value

a = A()
b = B()

dump(0) # simple objects
dump("string")
dump(callable)
dump(dump) # function

dump(A) # classes
dump(B)
dump(B.method)

dump(a) # instances
dump(b)
dump(b.method)

0 is *not* callable
string is *not* callable
<built-in function callable> is callable
<function dump at 8ca320> is callable
A is callable
B is callable
<unbound method A.method> is callable
<A instance at 8caa10> is *not* callable
<B instance at 8cab00> is callable
<method A.method of B instance at 8cab00> is callable

注意類物件 (A 和 B) 都是可呼叫的; 如果呼叫它們, 就產生新的物件(類例項). 但是 A 類的例項不可呼叫, 因為它的類沒有實現 _ _call_ _ 方法.

你可以在 operator 模組中找到檢查物件是否為某一內建型別(數字, 序列, 或者字典等) 的函式. 但是, 因為建立一個類很簡單(比如實現基本序列方法的類), 所以對這些 型別使用顯式的型別判斷並不是好主意.

在處理類和例項的時候會複雜些. Python 不會把類作為本質上的型別對待; 相反地, 所有的類都屬於一個特殊的類型別(special class type), 所有的類例項屬於一個特殊的例項型別(special instance type).

這意味著你不能使用 type 函式來測試一個例項是否屬於一個給定的類; 所有的例項都是同樣 的型別! 為了解決這個問題, 你可以使用 isinstance 函式,它會檢查一個物件是 不是給定類(或其子類)的例項. Example 1-15 展示了 isinstance 函式的使用.

1.2.4.4. Example 1-15. 使用 isinstance 函式

File: builtin-isinstance-example-1.py

class A:
    pass

class B:
    pass

class C(A):
    pass

class D(A, B):
    pass

def dump(object):
    print object, "=>",
    if isinstance(object, A):
        print "A",
    if isinstance(object, B):
        print "B",
    if isinstance(object, C):
        print "C",
    if isinstance(object, D):
        print "D",
    print

a = A()
b = B()
c = C()
d = D()

dump(a)
dump(b)
dump(c)
dump(d)
dump(0)
dump("string")

<A instance at 8ca6d0> => A
<B instance at 8ca750> => B
<C instance at 8ca780> => A C
<D instance at 8ca7b0> => A B D
0 =>
string =>

issubclass 函式與此相似, 它用於檢查一個類物件是否與給定類相同, 或者是給定類的子類. 如 Example 1-16 所示.

注意, isinstance 可以接受任何物件作為引數, 而 issubclass 函式在接受非類物件參 數時會引發 TypeError 異常.

1.2.4.5. Example 1-16. 使用 issubclass 函式

File: builtin-issubclass-example-1.py

class A:
    pass

class B:
    pass

class C(A):
    pass

class D(A, B):
    pass

def dump(object):
    print object, "=>",
    if issubclass(object, A):
        print "A",
    if issubclass(object, B):
        print "B",
    if issubclass(object, C):
        print "C",
    if issubclass(object, D):
        print "D",
    print

dump(A)
dump(B)
dump(C)
dump(D)
dump(0)
dump("string")

A => A
B => B
C => A C
D => A B D
0 =>
Traceback (innermost last):
  File "builtin-issubclass-example-1.py", line 29, in ?
  File "builtin-issubclass-example-1.py", line 15, in dump
TypeError: arguments must be classes

1.2.5. 計算 Python 表示式

Python 提供了在程式中與直譯器互動的多種方法. 例如 eval 函式將一個字串 作為 Python 表示式求值. 你可以傳遞一串文字, 簡單的表示式, 或者使用 內建 Python 函式. 如 Example 1-17 所示.

1.2.5.1. Example 1-17. 使用 eval 函式

File: builtin-eval-example-1.py

def dump(expression):
    result = eval(expression)
    print expression, "=>", result, type(result)

dump("1")
dump("1.0")
dump("'string'")
dump("1.0 + 2.0")
dump("'*' * 10")
dump("len('world')")

1 => 1 <type 'int'>
1.0 => 1.0 <type 'float'>
'string' => string <type 'string'>
1.0 + 2.0 => 3.0 <type 'float'>
'*' * 10 => ********** <type 'string'>
len('world') => 5 <type 'int'>

如果你不確定字串來源的安全性, 那麼你在使用 eval 的時候會遇到些麻煩. 例如, 某個使用者可能會使用 _ _import_ _ 函式載入 os 模組, 然後從硬碟刪除檔案 (如 Example 1-18 所示).

1.2.5.2. Example 1-18. 使用 eval 函式執行任意命令

File: builtin-eval-example-2.py

print eval("_ _import_ _('os').getcwd()")
print eval("_ _import_ _('os').remove('file')")

/home/fredrik/librarybook
Traceback (innermost last):
 File "builtin-eval-example-2", line 2, in ?
 File "<string>", line 0, in ?
os.error: (2, 'No such file or directory')

這裡我們得到了一個 os.error 異常, 這說明 Python 事實上在嘗試刪除檔案!

幸運地是, 這個問題很容易解決. 你可以給 eval 函式傳遞第 2 個引數, 一個定義了該表示式求值時名稱空間的字典. 我們測試下, 給函式傳遞個空字典:

>>> print eval("_ _import_ _('os').remove('file')", {})
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<string>", line 0, in ?
os.error: (2, 'No such file or directory')

呃.... 我們還是得到了個 os.error 異常.

這是因為 Python 在求值前會檢查這個字典, 如果沒有發現名稱為 _ _builtins_ _ 的變數(複數形式), 它就會新增一個:

>>> namespace = {}
>>> print eval("_ _import_ _('os').remove('file')", namespace)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "<string>", line 0, in ?
os.error: (2, 'No such file or directory')
>>> namespace.keys()
['_ _builtins_ _']

如果你列印這個 namespace 的內容, 你會發現裡邊有所有的內建函式.

[!Feather 注: 如果我RP不錯的話, 新增的這個_ _builtins_ _就是當前的_ _builtins_ _]

我們注意到瞭如果這個變數存在, Python 就不會去新增預設的, 那麼我們的解決方法也來了, 為傳遞的字典引數加入一個 _ _builtins_ _ 項即可. 如 Example 1-19 所示.

1.2.5.3. Example 1-19. 安全地使用 eval 函式求值

File: builtin-eval-example-3.py

print eval("_ _import_ _('os').getcwd()", {})
print eval("_ _import_ _('os').remove('file')", {"_ _builtins_ _": {}})

/home/fredrik/librarybook
Traceback (innermost last):
  File "builtin-eval-example-3.py", line 2, in ?
  File "<string>", line 0, in ?
NameError: _ _import_ _

即使這樣, 你仍然無法避免針對 CPU 和記憶體資源的攻擊. (比如, 形如 eval("'*'*1000000*2*2*2*2*2*2*2*2*2") 的語句在執行後會使你的程式耗盡系統資源).

1.2.6. 編譯和執行程式碼

eval 函式只針對簡單的表示式. 如果要處理大塊的程式碼, 你應該使用 compile 和 exec 函式 (如 Example 1-20 所示).

1.2.6.1. Example 1-20. 使用 compile 函式檢查語法

File: builtin-compile-example-1.py

NAME = "script.py"

BODY = """
prnt 'owl-stretching time'
"""

try:
    compile(BODY, NAME, "exec")
except SyntaxError, v:
    print "syntax error:", v, "in", NAME

# syntax error: invalid syntax in script.py

成功執行後, compile 函式會返回一個程式碼物件, 你可以使用 exec 語句執行它, 參見 Example 1-21 .

1.2.6.2. Example 1-21. 執行已編譯的程式碼

File: builtin-compile-example-2.py

BODY = """
print 'the ant, an introduction'
"""

code = compile(BODY, "<script>", "exec")

print code

exec code

<code object ? at 8c6be0, file "<script>", line 0>
the ant, an introduction

使用 Example 1-22 中的類可以在程式執行時實時地生成程式碼. write 方法用於新增程式碼, indent 和 dedent 方法用於控制縮排結構. 其他部分交給類來處理.

1.2.6.3. Example 1-22. 簡單的程式碼生成工具

File: builtin-compile-example-3.py

import sys, string

class CodeGeneratorBackend:
    "Simple code generator for Python"

    def begin(self, tab="/t"):
        self.code = []
        self.tab = tab
        self.level = 0

    def end(self):
        self.code.append("") # make sure there's a newline at the end 
        return compile(string.join(self.code, "/n"), "<code>", "exec")

    def write(self, string):
        self.code.append(self.tab * self.level + string)

    def indent(self):
        self.level = self.level + 1
        # in 2.0 and later, this can be written as: self.level += 1

    def dedent(self):
        if self.level == 0:
            raise SyntaxError, "internal error in code generator"
        self.level = self.level - 1
        # or: self.level -= 1

#
# try it out!

c = CodeGeneratorBackend()
c.begin()
c.write("for i in range(5):")
c.indent()
c.write("print 'code generation made easy!'")
c.dedent()
exec c.end()

code generation made easy!
code generation made easy!
code generation made easy!
code generation made easy!
code generation made easy!

Python 還提供了 execfile 函式, 一個從檔案載入程式碼, 編譯程式碼, 執行程式碼的快捷方式. Example 1-23 簡單地展示瞭如何使用這個函式.

1.2.6.4. Example 1-23. 使用 execfile 函式

File: builtin-execfile-example-1.py

execfile("hello.py")

def EXECFILE(filename, locals=None, globals=None):
    exec compile(open(filename).read(), filename, "exec") in locals, globals

EXECFILE("hello.py")

hello again, and welcome to the show
hello again, and welcome to the show

1.2.6.5. Example 1-24. hello.py 指令碼

File: hello.py

print "hello again, and welcome to the show"

1.2.7. 從 _ _builtin_ _ 模組過載函式

因為 Python 在檢查區域性名稱空間和模組名稱空間前不會檢查內建函式, 所以有時候你可能要顯式地引用 _ _builtin_ _ 模組. 例如 Example 1-25 過載了內建的 open 函式. 這時候要想使用原來的 open 函式, 就需要指令碼顯式地指明模組名稱.

1.2.7.1. Example 1-25. 顯式地訪問 _ _builtin_ _ 模組中的函式

File: builtin-open-example-1.py

def open(filename, mode="rb"):
    import _ _builtin_ _
    file = _ _builtin_ _.open(filename, mode)
    if file.read(5) not in("GIF87", "GIF89"):
        raise IOError, "not a GIF file"
    file.seek(0)
    return file

fp = open("samples/sample.gif")
print len(fp.read()), "bytes"

fp = open("samples/sample.jpg")
print len(fp.read()), "bytes"

3565 bytes
Traceback (innermost last):
  File "builtin-open-example-1.py", line 12, in ?
  File "builtin-open-example-1.py", line 5, in open
IOError: not a GIF file
[!Feather 注: 明白這個open()函式是幹什麼的麼? 檢查一個檔案是否是 GIF 檔案, 
一般如這類的圖片格式都在檔案開頭有預設的格式. 
另外開啟檔案推薦使用file()而不是open() , 雖然暫時沒有區別]

1.3. exceptions 模組

exceptions 模組提供了標準異常的層次結構. Python 啟動的時候會自動匯入這個模組, 並且將它加入到 _ _builtin_ _ 模組中. 也就是說, 一般不需要手動匯入這個模組.

在 1.5.2 版本時它是一個普通模組, 2.0 以及以後版本成為內建模組.

該模組定義了以下標準異常:

  • Exception 是所有異常的基類. 強烈建議(但不是必須)自定義的異常異常也繼承這個類.
  • SystemExit(Exception) 由 sys.exit 函式引發. 如果它在最頂層沒有被 try-except 語句捕獲, 那麼直譯器將直接關閉而不會顯示任何跟蹤返回資訊.
  • StandardError(Exception) 是所有內建異常的基類(除 SystemExit 外).
  • KeyboardInterrupt(StandardError) 在使用者按下 Control-C(或其他打斷按鍵)後 被引發. 如果它可能會在你使用 "捕獲所有" 的 try-except 語句時導致奇怪的問題.
  • ImportError(StandardError) 在 Python 匯入模組失敗時被引發.
  • EnvironmentError 作為所有直譯器環境引發異常的基類. (也就是說, 這些異常一般不是由於程式 bug 引起).
  • IOError(EnvironmentError) 用於標記 I/O 相關錯誤.
  • OSError(EnvironmentError) 用於標記 os 模組引起的錯誤.
  • WindowsError(OSError) 用於標記 os 模組中 Windows 相關錯誤.
  • NameError(StandardError) 在 Python 查詢全域性或區域性名稱失敗時被引發.
  • UnboundLocalError(NameError) , 當一個區域性變數還沒有賦值就被使用時, 會引發這個異常. 這個異常只有在2.0及之後的版本有; 早期版本只會引發一個普通的 NameError .
  • AttributeError(StandardError) , 當 Python 尋找(或賦值)給一個例項屬性, 方法, 模組功能或其它有效的命名失敗時, 會引發這個異常.
  • SyntaxError(StandardError) , 當直譯器在編譯時遇到語法錯誤, 這個異常就被引發.
  • (2.0 及以後版本) IndentationError(SyntaxError) 在遇到非法的縮排時被引發. 該異常只用於 2.0 及以後版本, 之前版本會引發一個 SyntaxError 異常.
  • (2.0 及以後版本) TabError(IndentationError) , 當使用 -tt 選項檢查不一致縮排時有可能被引發. 該異常只用於 2.0 及以後版本, 之前版本會引發一個SyntaxError 異常.
  • TypeError(StandardError) , 當給定型別的物件不支援一個操作時被引發.
  • AssertionError(StandardError) 在 assert 語句失敗時被引發(即表示式為 false 時).
  • LookupError(StandardError) 作為序列或字典沒有包含給定索引或鍵時所引發異常的基類.
  • IndexError(LookupError) , 當序列物件使用給定索引數索引失敗時(不存在索引對應物件)引發該異常.
  • KeyError(LookupError) 當字典物件使用給定索引索引失敗時(不存在索引對應物件)引發該異常.
  • ArithmeticError(StandardError) 作為數學計算相關異常的基類.
  • OverflowError(ArithmeticError) 在操作溢位時被引發(例如當一個整數太大, 導致不能符合給定型別).
  • ZeroDivisionError(ArithmeticError) , 當你嘗試用 0 除某個數時被引發.
  • FloatingPointError(ArithmeticError) , 當浮點數操作失敗時被引發.
  • ValueError(StandardError) , 當一個引數型別正確但值不合法時被引發.
  • (2.0 及以後版本) UnicodeError(ValueError) , Unicode 字串型別相關異常. 只使用在 2.0 及以後版本.
  • RuntimeError(StandardError) , 當出現執行時問題時引發, 包括在限制模式下嘗試訪問外部內容, 未知的硬體問題等等.
  • NotImplementedError(RuntimeError) , 用於標記未實現的函式, 或無效的方法.
  • SystemError(StandardError) , 直譯器內部錯誤. 該異常值會包含更多的細節 (經常會是一些深層次的東西, 比如 "eval_code2: NULL globals" ). 這本書的作者編了 5 年程式都沒見過這個錯誤. (想必是沒有用 raise SystemError).
  • MemoryError(StandardError) , 當直譯器耗盡記憶體時會引發該異常. 注意只有在底層記憶體分配抱怨時這個異常才會發生; 如果是在你的舊機器上, 這個異常發生之前系統會陷入混亂的記憶體交換中.

你可以建立自己的異常類. 只需要繼承內建的 Exception 類(或者它的任意一個合適的子類)即可, 有需要時可以再過載它的 _ _str_ _ 方法. Example 1-26 展示瞭如何使用 exceptions 模組.

1.3.0.1. Example 1-26. 使用 exceptions 模組

File: exceptions-example-1.py

# python imports this module by itself, so the following
# line isn't really needed
# python 會自動匯入該模組, 所以以下這行是不必要的
# import exceptions

class HTTPError(Exception):
    # indicates an HTTP protocol error
    def _ _init_ _(self, url, errcode, errmsg):
        self.url = url
        self.errcode = errcode
        self.errmsg = errmsg
    def _ _str_ _(self):
        return (
            "<HTTPError for %s: %s %s>" %
            (self.url, self.errcode, self.errmsg)
            )

try:
    raise HTTPError("http://www.python.org/foo", 200, "Not Found")
except HTTPError, error:
    print "url", "=>", error.url
    print "errcode", "=>", error.errcode
    print "errmsg", "=>", error.errmsg
    raise # reraise exception

url => http://www.python.org/foo
errcode => 200
errmsg => Not Found
Traceback (innermost last):
  File "exceptions-example-1", line 16, in ?
HTTPError: <HTTPError for http://www.python.org/foo: 200 Not Found>

1.4. os 模組

這個模組中的大部分函式通過對應平臺相關模組實現, 比如 posix 和 nt. os 模組會在第一次匯入的時候自動載入合適的執行模組.

1.4.1. 處理檔案

內建的 open / file 函式用於建立, 開啟和編輯檔案, 如 Example 1-27 所示. 而 os 模組提供了重新命名和刪除檔案所需的函式.

1.4.1.1. Example 1-27. 使用 os 模組重新命名和刪除檔案

File: os-example-3.py

import os
import string

def replace(file, search_for, replace_with):
    # replace strings in a text file

    back = os.path.splitext(file)[0] + ".bak"
    temp = os.path.splitext(file)[0] + ".tmp"

    try:
        # remove old temp file, if any
        os.remove(temp)
    except os.error:
        pass

    fi = open(file)
    fo = open(temp, "w")

    for s in fi.readlines():
        fo.write(string.replace(s, search_for, replace_with))

    fi.close()
    fo.close()

    try:
        # remove old backup file, if any
        os.remove(back)
    except os.error:
        pass

    # rename original to backup...
    os.rename(file, back)

    # ...and temporary to original
    os.rename(temp, file)

#
# try it out!

file = "samples/sample.txt"

replace(file, "hello", "tjena")
replace(file, "tjena", "hello")

1.4.2. 處理目錄

os 模組也包含了一些用於目錄處理的函式.

listdir 函式返回給定目錄中所有檔名(包括目錄名)組成的列表, 如 Example 1-28 所示. 而 Unix 和 Windows 中使用的當前目錄和父目錄標記(. 和 .. )不包含在此列表中.

1.4.2.1. Example 1-28. 使用 os 列出目錄下的檔案

File: os-example-5.py

import os

for file in os.listdir("samples"):
    print file

sample.au
sample.jpg
sample.wav
...

getcwd 和 chdir 函式分別用於獲得和改變當前工作目錄. 如 Example 1-29 所示.

1.4.2.2. Example 1-29. 使用 os 模組改變當前工作目錄

File: os-example-4.py

import os

# where are we?
cwd = os.getcwd()
print "1", cwd

# go down
os.chdir("samples")
print "2", os.getcwd()

# go back up
os.chdir(os.pardir)
print "3", os.getcwd()

1 /ematter/librarybook
2 /ematter/librarybook/samples
3 /ematter/librarybook

makedirs 和 removedirs 函式用於建立或刪除目錄層,如 Example 1-30 所示.

1.4.2.3. Example 1-30. 使用 os 模組建立/刪除多個目錄級

File: os-example-6.py

import os

os.makedirs("test/multiple/levels")

fp = open("test/multiple/levels/file", "w")
fp.write("inspector praline")
fp.close()

# remove the file
os.remove("test/multiple/levels/file")

# and all empty directories above it
os.removedirs("test/multiple/levels")

removedirs 函式會刪除所給路徑中最後一個目錄下所有的空目錄. 而 mkdir 和 rmdir 函式只能處理單個目錄級. 如 Example 1-31 所示.

1.4.2.4. Example 1-31. 使用 os 模組建立/刪除目錄

File: os-example-7.py

import os

os.mkdir("test")
os.rmdir("test")

os.rmdir("samples") # this will fail

Traceback (innermost last):
  File "os-example-7", line 6, in ?
OSError: [Errno 41] Directory not empty: 'samples'

如果需要刪除非空目錄, 你可以使用 shutil 模組中的 rmtree 函式.

1.4.3. 處理檔案屬性

stat 函式可以用來獲取一個存在檔案的資訊, 如 Example 1-32 所示. 它返回一個類元組物件(stat_result物件, 包含 10 個元素), 依次是st_mode (許可權模式), st_ino (inode number), st_dev (device), st_nlink (number of hard links), st_uid (所有者使用者 ID), st_gid (所有者所在組 ID ), st_size (檔案大小, 位元組), st_atime (最近一次訪問時間), st_mtime (最近修改時間), st_ctime (平臺相關; Unix下的最近一次元資料/metadata修改時間, 或者 Windows 下的建立時間) - 以上專案也可作為屬性訪問.

[!Feather 注: 原文為 9 元元組. 另,返回物件並非元組型別,為 struct.]

1.4.3.1. Example 1-32. 使用 os 模組獲取檔案屬性

File: os-example-1.py

import os
import time

file = "samples/sample.jpg"

def dump(st):
    mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime = st
    print "- size:", size, "bytes"
    print "- owner:", uid, gid
    print "- created:", time.ctime(ctime)
    print "- last accessed:", time.ctime(atime)
    print "- last modified:", time.ctime(mtime)
    print "- mode:", oct(mode)
    print "- inode/dev:", ino, dev

#
# get stats for a filename

st = os.stat(file)

print "stat", file
dump(st)
print

#
# get stats for an open file

fp = open(file)

st = os.fstat(fp.fileno())

print "fstat", file
dump(st)

stat samples/sample.jpg
- size: 4762 bytes
- owner: 0 0
- created: Tue Sep 07 22:45:58 1999
- last accessed: Sun Sep 19 00:00:00 1999
- last modified: Sun May 19 01:42:16 1996
- mode: 0100666
- inode/dev: 0 2

fstat samples/sample.jpg
- size: 4762 bytes
- owner: 0 0
- created: Tue Sep 07 22:45:58 1999
- last accessed: Sun Sep 19 00:00:00 1999
- last modified: Sun May 19 01:42:16 1996
- mode: 0100666
- inode/dev: 0 0

返回物件中有些屬性在非 Unix 平臺下是無意義的, 比如 (st_inode , st_dev)為 Unix 下的為每個檔案提供了唯一標識, 但在其他平臺可能為任意無意義資料 .

stat 模組包含了很多可以處理該返回物件的常量及函式. 下面的程式碼展示了其中的一些.

可以使用 chmod 和 utime 函式修改檔案的許可權模式和時間屬性,如 Example 1-33 所示.

1.4.3.2. Example 1-33. 使用 os 模組修改檔案的許可權和時間戳

File: os-example-2.py

import os
import stat, time

infile = "samples/sample.jpg"
outfile = "out.jpg"

# copy contents
fi = open(infile, "rb")
fo = open(outfile, "wb")

while 1:
    s = fi.read(10000)
    if not s:
        break
    fo.write(s)

fi.close()
fo.close()

# copy mode and timestamp
st = os.stat(infile)
os.chmod(outfile, stat.S_IMODE(st[stat.ST_MODE]))
os.utime(outfile, (st[stat.ST_ATIME], st[stat.ST_MTIME]))

print "original", "=>"
print "mode", oct(stat.S_IMODE(st[stat.ST_MODE]))
print "atime", time.ctime(st[stat.ST_ATIME])
print "mtime", time.ctime(st[stat.ST_MTIME])

print "copy", "=>"
st = os.stat(outfile)
print "mode", oct(stat.S_IMODE(st[stat.ST_MODE]))
print "atime", time.ctime(st[stat.ST_ATIME])
print "mtime", time.ctime(st[stat.ST_MTIME])

original =>
mode 0666
atime Thu Oct 14 15:15:50 1999
mtime Mon Nov 13 15:42:36 1995
copy =>
mode 0666
atime Thu Oct 14 15:15:50 1999
mtime Mon Nov 13 15:42:36 1995

1.4.4. 處理程序

system 函式在當前程序下執行一個新命令, 並等待它完成, 如 Example 1-34 所示.

1.4.4.1. Example 1-34. 使用 os 執行作業系統命令

File: os-example-8.py

import os

if os.name == "nt":
    command = "dir"
else:
    command = "ls -l"

os.system(command)

-rwxrw-r--   1 effbot  effbot        76 Oct  9 14:17 README
-rwxrw-r--   1 effbot  effbot      1727 Oct  7 19:00 SimpleAsyncHTTP.py
-rwxrw-r--   1 effbot  effbot       314 Oct  7 20:29 aifc-example-1.py
-rwxrw-r--   1 effbot  effbot       259 Oct  7 20:38 anydbm-example-1.py
...

命令通過作業系統的標準 shell 執行, 並返回 shell 的退出狀態. 需要注意的是在 Windows 95/98 下, shell 通常是 command.com , 它的推出狀態總是 0.

由於 11os.system11 直接將命令傳遞給 shell , 所以如果你不檢查傳入引數的時候會很危險 (比如命令 os.system("viewer %s" % file), 將 file 變數設定為 "sample.jpg; rm -rf $HOME" ....). 如果不確定引數的安全性, 那麼最好使用 exec 或 spawn 代替(稍後介紹).

exec 函式會使用新程序替換當前程序(或者說是"轉到程序"). 在 Example 1-35 中, 字串 "goodbye" 永遠不會被列印.

1.4.4.2. Example 1-35. 使用 os 模組啟動新程序

File: os-exec-example-1.py

import os
import sys

program = "python"
arguments = ["hello.py"]

print os.execvp(program, (program,) +  tuple(arguments))
print "goodbye"

hello again, and welcome to the show

Python 提供了很多表現不同的 exec 函式. Example 1-35 使用的是 execvp 函式, 它會從標準路徑搜尋執行程式, 把第二個引數(元組)作為單獨的引數傳遞給程式, 並使用當前的環境變數來執行程式. 其他七個同類型函式請參閱 Python Library Reference .

在 Unix 環境下, 你可以通過組合使用 exec , fork 以及 wait 函式來從當前程式呼叫另一個程式, 如 Example 1-36 所示. fork 函式複製當前程序, wait 函式會等待一個子程序執行結束.

1.4.4.3. Example 1-36. 使用 os 模組呼叫其他程式 (Unix)

File: os-exec-example-2.py

import os
import sys

def run(program, *args):
    pid = os.fork()
    if not pid:
        os.execvp(program, (program,) +  args)
    return os.wait()[0]

run("python", "hello.py")

print "goodbye"

hello again, and welcome to the show
goodbye