python 幫助文檔、自我解釋

現在讓我們以交互方式使用 Python 來開始研究。當我們從命令行啓動 Python 時,就進入了 Python shell,在這裏可以輸入 Python 代碼,而且立刻會從 Python 解釋器獲得響應。

清單 1. 以交互方式啓動 Python 解釋器
Python 2.7.15rc1 (default, Nov 12 2018, 14:31:15) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 

在讓 Python 運行起來,並看到 Python 提示符( >>> )之後,您可能想知道 Python 能識別什麼字。大多數編程語言都有保留字或關鍵字,這些字在該語言中有特殊的意義,Python 也不例外。您可能還注意到,Python 建議我們輸入 help 以獲取更多信息。也許我們可以向 Python 尋求一些關於關鍵字的幫助。

Python 的聯機幫助實用程序

讓我們按建議的那樣,通過輸入 help 來開始討論,並觀察它是否會向我們提供任何關於關鍵字的線索:

清單 2. 向 Python 尋求幫助
`>>> help`

`Type help() for interactive help, or help(object) for help about object.`

因爲我們不知道哪些對象可能包含關鍵字,所以不指定任何特定對象來嘗試 help() :

清單 3. 啓動幫助實用程序
`>>> help()`

`Welcome to Python 2.2!  This is the online help utility.`

`If this is your first time using Python, you should definitely check out`

`the tutorial on the Internet at http://www.python.org/doc/tut/.`

`Enter the name of any module, keyword, or topic to get help on writing`

`Python programs and using Python modules.  To quit this help utility and`

`return to the interpreter, just type "quit".`

`To get a list of available modules, keywords, or topics, type "modules",`

`"keywords", or "topics".  Each module also comes with a one-line summary`

`of what it does; to list the modules whose summaries contain a given word`

`such as "spam", type "modules spam".`

`help>`

現在,我們對此的理解似乎深入了些。讓我們在 help 提示符下輸入 keywords :

清單 4. 用 keywords 尋求幫助
`help> keywords`

Here is a list of the Python keywords.  Enter any keyword to get more help.

and                 elif                if                  print
as                  else                import              raise
assert              except              in                  return
break               exec                is                  try
class               finally             lambda              while
continue            for                 not                 with
def                 from                or                  yield
del                 global              pass     

`help> quit()

`You are now leaving help and returning to the Python interpreter.`

`If you want to ask for help on a particular object directly from the`

`interpreter, you can type "help(object)".  Executing "help('string')"`

`has the same effect as typing a particular string at the help> prompt.`

`>>>`

輸入 help() 後,會看到一條歡迎消息和一些指示信息,接着是 help 提示符。在提示符下輸入 keywords ,則會看到一個 Python 關鍵字列表。我們已經獲得了問題的答案,於是退出幫助實用程序,這時會看到一條簡短的告別消息,並返回到 Python 提示符下。

正如您從這個示例可以看到的,Python 的聯機幫助實用程序會顯示關於各種主題或特定對象的信息。幫助實用程序很有用,並確實利用了 Python 的自省能力。但僅僅使用幫助不會揭示幫助是如何獲得其信息的。而且,因爲本文的目的是揭示 Python 自省的所有祕密,所以我們必須迅速地跳出對幫助實用程序的討論。

在結束關於幫助的討論之前,讓我們用它來獲得一個可用模塊的列表。模塊只是包含 Python 代碼的文本文件,其名稱後綴是.py 。如果在 Python 提示符下輸入 help('modules') ,或在 help 提示符下輸入 modules ,則會看到一長列可用模塊,類似於下面所示的部分列表。自己嘗試它以觀察您的系統中有哪些可用模塊,並瞭解爲什麼會認爲 Python 是“自帶電池”的。

清單 5. 部分可用模塊的列表
`>>> help('modules')`

`Please wait a moment while I gather a list of all available modules...`

`BaseHTTPServer      cgitb               marshal             sndhdr`

`Bastion             chunk               math                socket`

`CDROM               cmath               md5                 sre`

`CGIHTTPServer       cmd                 mhlib               sre_compile`

`Canvas              code                mimetools           sre_constants`

`<...>`

`bisect              macpath             signal              xreadlines`

`cPickle             macurl2path         site                xxsubtype`

`cStringIO           mailbox             slgc (package)      zipfile`

`calendar            mailcap             smtpd`

`cgi                 markupbase          smtplib`

`Enter any module name to get more help.  Or, type "modules spam" to search`

`for modules whose descriptions contain the word "spam".`

`>>>`

sys 模塊

sys 模塊是提供關於 Python 本身的詳盡內在信息的模塊。通過導入模塊,並用點(.)符號引用其內容(如變量、函數和類)來使用模塊。 sys 模塊包含各種變量和函數,它們揭示了當前的 Python 解釋器有趣的詳細信息。讓我們研究其中的一部分。我們要再次以交互方式運行 Python,並在 Python 命令提示符下輸入命令。首先,我們將導入 sys 模塊。然後,我們會輸入sys.executable 變量,它包含到 Python 解釋器的路徑:

清單 6. 導入 sys 模塊
`$ python`

`Python 2.2.2 (#1, Oct 28 2002, 17:22:19)`

`[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2`

`Type "help", "copyright", "credits" or "license" for more information.`

`>>> import sys`

`>>> sys.executable`

`'/usr/local/bin/python'`

當輸入一行只包含對象名稱的代碼時,Python 通過顯示該對象的表示進行響應,對於簡單對象,往往顯示對象的值。在本例中,因爲所顯示的值是用引號括起來的,所以我們得到一條線索: sys.executable 可能是字符串對象。稍後,我們將研究確定對象類型的其它更精確的方法,但只在 Python 提示符下輸入對象名稱是一種迅速而又方便的自省形式。

讓我們研究 sys 模塊其它一些有用的屬性。

platform 變量告訴我們現在處於什麼操作系統上:

sys.platform 屬性
`>>> sys.platform`

`'linux2'`

在當前的 Python 中,版本以字符串和元組(元組包含對象序列)來表示:

清單 8. sys.version 和 sys.version_info 屬性
`>>> sys.version`

`'2.2.2 (#1, Oct 28 2002, 17:22:19) \n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)]'`

`>>> sys.version_info`

`(2, 2, 2, 'final', 0)`

maxint 變量反映了可用的最大整數值:

sys.maxint 屬性

`>>> sys.maxint`

`2147483647`

argv 變量是一個包含命令行參數的列表(如果參數被指定的話)。第一項 argv[0] 是所運行腳本的路徑。當我們以交互方式運行 Python 時,這個值是空字符串:

清單 10. sys.argv 屬性

`>>> sys.argv`

`['']`

當運行其它 Python shell 時,如 PyCrust ,會看到類似於下面的信息:

清單 11. 使用 PyCrust 時的 sys.argv 屬性

`>>> sys.argv[0]`

`'/home/pobrien/Code/PyCrust/PyCrustApp.py'`

path 變量是模塊搜索路徑,Python 在導入期間將在其中的目錄列表中尋找模塊。最前面的空字符串 '' 是指當前目錄:

清單 12. sys.path 屬性

`>>> sys.path`

`['', '/home/pobrien/Code',`

`'/usr/local/lib/python2.2',`

`'/usr/local/lib/python2.2/plat-linux2',`

`'/usr/local/lib/python2.2/lib-tk',`

`'/usr/local/lib/python2.2/lib-dynload',`

`'/usr/local/lib/python2.2/site-packages']`

modules 變量是一個字典,它將當前已裝入的所有模塊的名稱映射到模塊對象。如您所見,缺省情況下,Python 裝入一些特定的模塊:

清單 13. sys.modules 屬性
`>>> sys.modules`

`{'stat': <``module` `'stat' from '/usr/local/lib/python2.2/stat.pyc'>,`

`'__future__': <``module` `'__future__' from '/usr/local/lib/python2.2/__future__.pyc'>,`

`'copy_reg': <``module` `'copy_reg' from '/usr/local/lib/python2.2/copy_reg.pyc'>,`

`'posixpath': <``module` `'posixpath' from '/usr/local/lib/python2.2/posixpath.pyc'>,`

`'UserDict': <``module` `'UserDict' from '/usr/local/lib/python2.2/UserDict.pyc'>,`

`'signal': <``module` `'signal' (built-in)>,`

`'site': <``module` `'site' from '/usr/local/lib/python2.2/site.pyc'>,`

`'__builtin__': <``module` `'__builtin__' (built-in)>,`

`'sys': <``module` `'sys' (built-in)>,`

`'posix': <``module` `'posix' (built-in)>,`

`'types': <``module` `'types' from '/usr/local/lib/python2.2/types.pyc'>,`

`'__main__': <``module` `'__main__' (built-in)>,`

`'exceptions': <``module` `'exceptions' (built-in)>,`

`'os': <``module` `'os' from '/usr/local/lib/python2.2/os.pyc'>,`

`'os.path': <``module` `'posixpath' from '/usr/local/lib/python2.2/posixpath.pyc'>}`

keyword 模塊

讓我們返回到關於 Python 關鍵字的問題。儘管幫助向我們顯示了關鍵字列表,但事實證明一些幫助信息是硬編碼的。關鍵字列表恰好是硬編碼的,但畢竟它的自省程度不深。讓我們研究一下,能否直接從 Python 標準庫的某個模塊中獲取這個信息。如果在 Python 提示符下輸入 help('modules keywords') ,則會看到如下信息:

清單 14. 同時使用 modules 和 keywords 尋求幫助

`>>> help('modules keywords')`

`Here is a list of matching modules.  Enter any module name to get more help.`

`keyword - Keywords (from "graminit.c")`

看起來, keyword 模塊好象包含關鍵字。在文本編輯器中打開 keyword.py 文件,我們可以看到,Python 確實可以把關鍵字列表顯式地用作 keyword 模塊的 kwlist 屬性。在 keyword 模塊的註釋中,我們還可以看到,該模塊是根據 Python 本身的源代碼自動生成的,這可以保證其關鍵字列表是準確而完整的:

清單 15. keyword 模塊的關鍵字列表

`>>> import keyword`

`>>> keyword.kwlist`

`['and', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else',`

`'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',`

`'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'yield']`

dir() 函數

儘管查找和導入模塊相對容易,但要記住每個模塊包含什麼卻不是這麼簡單。您並不希望總是必須查看源代碼來找出答案。幸運的是,Python 提供了一種方法,可以使用內置的 dir() 函數來檢查模塊(以及其它對象)的內容。

dir() 函數可能是 Python 自省機制中最著名的部分了。它返回傳遞給它的任何對象的屬性名稱經過排序的列表。如果不指定對象,則 dir() 返回當前作用域中的名稱。讓我們將 dir() 函數應用於 keyword 模塊,並觀察它揭示了什麼:

清單 16. keyword 模塊的屬性

`>>> dir(keyword)`

`['__all__', '__builtins__', '__doc__', '__file__', '__name__',`

`'iskeyword', 'keyword', 'kwdict', 'kwlist', 'main']`

那麼將它應用於我們先前討論的 sys 模塊會怎麼樣呢?

清單 17. sys 模塊的屬性
`>>> dir(sys)`

`['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__',`

`'__stdin__', '__stdout__', '_getframe', 'argv', 'builtin_module_names',`

`'byteorder', 'copyright', 'displayhook', 'exc_info', 'exc_type', 'excepthook',`

`'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags',`

`'getrecursionlimit', 'getrefcount', 'hexversion', 'last_traceback',`

`'last_type', 'last_value', 'maxint', 'maxunicode', 'modules', 'path',`

`'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags',`

`'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout',`

`'version', 'version_info', 'warnoptions']`

如果不帶任何參數,則 dir() 返回當前作用域中的名稱。請注意,因爲我們先前導入了 keyword 和 sys ,所以它們出現在列表中。導入模塊將把該模塊的名稱添加到當前作用域:

清單 18. 當前作用域中的名稱
`>>> dir()`

`['__builtins__', '__doc__', '__name__', 'keyword', 'sys']`

我們曾經提到 dir() 函數是內置函數,這意味着我們不必爲了使用該函數而導入模塊。不必做任何操作,Python 就可識別內置函數。現在,我們看到調用 dir() 後返回了這個名稱 __builtins__ 。也許此處有連接。讓我們在 Python 提示符下輸入名稱 __builtins__ ,並觀察 Python 是否會告訴我們關於它的任何有趣的事情:

清單 19. builtins 是什麼?

`>>> __builtins__`

`<``module` `'__builtin__' (built-in)>`

因此 __builtins__ 看起來象是當前作用域中綁定到名爲 __builtin__ 的模塊對象的名稱。(因爲模塊不是隻有多個單一值的簡單對象,所以 Python 改在尖括號中顯示關於模塊的信息。)注:如果您在磁盤上尋找 __builtin__.py 文件,將空手而歸。這個特殊的模塊對象是 Python 解釋器憑空創建的,因爲它包含着解釋器始終可用的項。儘管看不到物理文件,但我們仍可以將 dir() 函數應用於這個對象,以觀察所有內置函數、錯誤對象以及它所包含的幾個雜項屬性。

清單 20. builtins 模塊的屬性

`>>> dir(__builtins__)`

`['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning',`

`'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',`

`'FloatingPointError', 'IOError', 'ImportError', 'IndentationError',`

`'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError',`

`'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError',`

`'OverflowError', 'OverflowWarning', 'ReferenceError', 'RuntimeError',`

`'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError',`

`'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError',`

`'UnboundLocalError', 'UnicodeError', 'UserWarning', 'ValueError', 'Warning',`

`'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__',`

`'abs', 'apply', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp',`

`'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict',`

`'dir', 'divmod', 'eval', 'execfile', 'exit', 'file', 'filter', 'float',`

`'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int',`

`'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list',`

`'locals', 'long', 'map', 'max', 'min', 'object', 'oct', 'open', 'ord', 'pow',`

`'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round',`

`'setattr', 'slice', 'staticmethod', 'str', 'super', 'tuple', 'type', 'unichr',`

`'unicode', 'vars', 'xrange', 'zip']`

dir() 函數適用於所有對象類型,包括字符串、整數、列表、元組、字典、函數、定製類、類實例和類方法。讓我們將 dir()應用於字符串對象,並觀察 Python 返回什麼。如您所見,即使簡單的 Python 字符串也有許多屬性:

清單 21. 字符串屬性
 
`>>> dir('this is a string')`

`['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__',`

`'_ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__',`

`'__hash__', '__init__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',`

`'__new__', '__reduce__', '__repr__', '__rmul__', '__setattr__', '__str__',`

`'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs',`

`'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace',`

`'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'replace', 'rfind',`

`'rindex', 'rjust', 'rstrip', 'split', 'splitlines', 'startswith', 'strip',`

`'swapcase', 'title', 'translate', 'upper', 'zfill']`

自己嘗試下列示例以觀察它們返回什麼。注: # 字符標記註釋的開始。Python 將忽略從註釋開始部分到該行結束之間的所有內容:

清單 22. 將 dir() 運用於其它對象

`dir(42)   # Integer (and the meaning of life)`

`dir([])   # List (an empty list, actually)`

`dir(())   # Tuple (also empty)`

`dir({})   # Dictionary (ditto)`

`dir(dir)  # Function (functions are also objects)`

爲了說明 Python 自省能力的動態本質,讓我們研究將 dir() 運用於定製類和一些類實例的示例。我們將以交互方式定義自己的類,創建一些類的實例,僅向其中一個實例添加唯一的屬性,並觀察 Python 能否一直保存所有這些。以下是結果:

清單 23. 將 dir() 運用於定製類、類實例和屬性

`>>> class Person(object):`

`...     """Person class."""`

`...     def __init__(self, name, age):`

`...         self.name = name`

`...         self.age = age`

`...     def intro(self):`

`...         """Return an introduction."""`

`...         return "Hello, my name is %s and I'm %s." % (self.name, self.age)`

`...`

`>>> bob = Person("Robert", 35)   # Create a Person instance`

`>>> joe = Person("Joseph", 17)   # Create another`

`>>> joe.sport = "football"       # Assign a new attribute to one instance`

`>>> dir(Person)      # Attributes of the Person class`

`['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',`

`'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',`

`'__setattr__', '__str__', '__weakref__', 'intro']`

`>>> dir(bob)         # Attributes of bob`

`['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',`

`'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',`

`'__setattr__', '__str__', '__weakref__', 'age', 'intro', 'name']`

`>>> dir(joe)         # Note that joe has an additional attribute`

`['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__',`

`'__hash__', '__init__', '__module__', '__new__', '__reduce__', '__repr__',`

`'__setattr__', '__str__', '__weakref__', 'age', 'intro', 'name', 'sport']`

`>>> bob.intro()      # Calling bob's intro method`

`"Hello, my name is Robert and I'm 35."`

`>>> dir(bob.intro)   # Attributes of the intro method`

`['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__get__',`

`'__getattribute__', '__hash__', '__init__', '__new__', '__reduce__',`

`'__repr__', '__setattr__', '__str__', 'im_class', 'im_func', 'im_self']`

文檔字符串

在許多 dir() 示例中,您可能會注意到的一個屬性是 __doc__ 屬性。這個屬性是一個字符串,它包含了描述對象的註釋。Python 稱之爲文檔字符串或 docstring,以下是其工作原理。如果模塊、類、方法或函數定義的第一條語句是字符串,那麼該字符串會作爲對象的 __doc__ 屬性與該對象關聯起來。例如,看一下 __builtins__ 對象的文檔字符串。因爲文檔字符串通常包含嵌入的換行 \n ,我們將使用 Python 的 print 語句,以便輸出更易於閱讀:

清單 24. 模塊文檔字符串
`>>> print __builtins__.__doc__   # Module docstring`

`Built-in functions, exceptions, and other objects.`

`Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.`

Python 甚至再次維持了在 Python shell 中以交互方式定義的類和方法上的文檔字符串。讓我們研究 Person 類及其 intro 方法的文檔字符串:

清單 25. 類和方法文檔字符串

`>>> Person.__doc__         # Class docstring`

`'Person class.'`

`>>> Person.intro.__doc__   # Class method docstring`

`'Return an introduction.'`

因爲文檔字符串提供瞭如此有價值的信息,所以許多 Python 開發環境都有自動顯示對象的文檔字符串的方法。讓我們再看一個 dir() 函數的文檔字符串:

清單 26. 函數文檔字符串
`>>> print dir.__doc__   # Function docstring`

`dir([object]) -> list of strings`

`Return an alphabetized list of names comprising (some of) the attributes`

`of the given object, and of attributes reachable from it:`

`No argument:  the names in the current scope.`

`Module object:  the module attributes.`

`Type or class object:  its attributes, and recursively the attributes of`

`its bases.`

`Otherwise:  its attributes, its class's attributes, and recursively the`

`attributes of its class's base classes.`

檢查 Python 對象

我們好幾次提到了“對象(object)”這個詞,但一直沒有真正定義它。編程環境中的對象很象現實世界中的對象。實際的對象有一定的形狀、大小、重量和其它特徵。實際的對象還能夠對其環境進行響應、與其它對象交互或執行任務。計算機中的對象試圖模擬我們身邊現實世界中的對象,包括象文檔、日程表和業務過程這樣的抽象對象。

類似於實際的對象,幾個計算機對象可能共享共同的特徵,同時保持它們自己相對較小的變異特徵。想一想您在書店中看到的書籍。書籍的每個物理副本都可能有污跡、幾張破損的書頁或唯一的標識號。儘管每本書都是唯一的對象,但都擁有相同標題的每本書都只是原始模板的實例,並保留了原始模板的大多數特徵。

對於面向對象的類和類實例也是如此。例如,可以看到每個 Python 字符串都被賦予了一些屬性, dir() 函數揭示了這些屬性。在前一個示例中,我們定義了自己的 Person 類,它擔任創建個別 Person 實例的模板,每個實例都有自己的 name 和 age 值,同時共享自我介紹的能力。這就是面向對象。

於是在計算機術語中,對象是擁有標識和值的事物,屬於特定類型、具有特定特徵和以特定方式執行操作。並且,對象從一個或多個父類繼承了它們的許多屬性。除了關鍵字和特殊符號(象運算符,如 + 、 - 、 * 、 ** 、 / 、 % 、 < 、 > 等)外,Python 中的所有東西都是對象。Python 具有一組豐富的對象類型:字符串、整數、浮點、列表、元組、字典、函數、類、類實例、模塊、文件等。

當您有一個任意的對象(也許是一個作爲參數傳遞給函數的對象)時,可能希望知道一些關於該對象的情況。在本節中,我們將向您展示如何讓 Python 對象回答如下問題:

  • 對象的名稱是什麼?
  • 這是哪種類型的對象?
  • 對象知道些什麼?
  • 對象能做些什麼?
  • 對象的父對象是誰?

名稱

並非所有對象都有名稱,但那些有名稱的對象都將名稱存儲在其 __name__ 屬性中。注:名稱是從對象而不是引用該對象的變量中派生的。下面這個示例着重說明了這種區別:

清單 27. 名稱中有什麼?
`$ python`

`Python 2.2.2 (#1, Oct 28 2002, 17:22:19)`

`[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2`

`Type "help", "copyright", "credits" or "license" for more information.`

`>>> dir()                # The dir() function`

`['__builtins__', '__doc__', '__name__']`

`>>> directory = dir      # Create a new variable`

`>>> directory()          # Works just like the original object`

`['__builtins__', '__doc__', '__name__', 'directory']`

`>>> dir.__name__         # What's your name?`

`'dir'`

`>>> directory.__name__   # My name is the same`

`'dir'`

`>>> __name__             # And now for something completely different`

`'__main__'`

模塊擁有名稱,Python 解釋器本身被認爲是頂級模塊或主模塊。當以交互的方式運行 Python 時,局部 __name__ 變量被賦予值 '__main__' 。同樣地,當從命令行執行 Python 模塊,而不是將其導入另一個模塊時,其 __name__ 屬性被賦予值'__main__' ,而不是該模塊的實際名稱。這樣,模塊可以查看其自身的 __name__ 值來自行確定它們自己正被如何使用,是作爲另一個程序的支持,還是作爲從命令行執行的主應用程序。因此,下面這條慣用的語句在 Python 模塊中是很常見的:

清單 28. 用於執行或導入的測試

`if __name__ == '__main__':`

`# Do something appropriate here, like calling a`

`# main() function defined elsewhere in this module.`

`main()`

`else:`

`# Do nothing. This module has been imported by another`

`# module that wants to make use of the functions,`

`# classes and other useful bits it has defined.`

類型

type() 函數有助於我們確定對象是字符串還是整數,或是其它類型的對象。它通過返回類型對象來做到這一點,可以將這個類型對象與 types 模塊中定義的類型相比較:

清單 29. 我是您的類型嗎?
`>>> import types`

`>>> print types.__doc__`

`Define names for all type symbols known in the standard interpreter.`

`Types that are part of optional modules (e.g. array) are not listed.`

`>>> dir(types)`

`['BufferType', 'BuiltinFunctionType', 'BuiltinMethodType', 'ClassType',`

`'CodeType', 'ComplexType', 'DictProxyType', 'DictType', 'DictionaryType',`

`'EllipsisType', 'FileType', 'FloatType', 'FrameType', 'FunctionType',`

`'GeneratorType', 'InstanceType', 'IntType', 'LambdaType', 'ListType',`

`'LongType', 'MethodType', 'ModuleType', 'NoneType', 'ObjectType', 'SliceType',`

`'StringType', 'StringTypes', 'TracebackType', 'TupleType', 'TypeType',`

`'UnboundMethodType', 'UnicodeType', 'XRangeType', '__builtins__', '__doc__',`

`'__file__', '__name__']`

`>>> s = 'a sample string'`

`>>> type(s)`

`<``type` `'str'>`

`>>> if type(s) is types.StringType: print "s is a string"`

`...`

`s is a string`

`>>> type(42)`

`<``type` `'int'>`

`>>> type([])`

`<``type` `'list'>`

`>>> type({})`

`<``type` `'dict'>`

`>>> type(dir)`

`<``type` `'builtin_function_or_method'>`

標識

先前我們說過,每個對象都有標識、類型和值。值得注意的是,可能有多個變量引用同一對象,同樣地,變量可以引用看起來相似(有相同的類型和值),但擁有截然不同標識的多個對象。當更改對象時(如將某一項添加到列表),這種關於對象標識的概念尤其重要,如在下面的示例中, blist 和 clist 變量引用同一個列表對象。正如您在示例中所見, id() 函數給任何給定對象返回唯一的標識符:

清單 30. 目的地……
`>>> print id.__doc__`

`id(object) -> integer`

`Return the identity of an object.  This is guaranteed to be unique among`

`simultaneously existing objects.  (Hint: it's the object's memory address.)`

`>>> alist = [1, 2, 3]`

`>>> blist = [1, 2, 3]`

`>>> clist = blist`

`>>> clist`

`[1, 2, 3]`

`>>> blist`

`[1, 2, 3]`

`>>> alist`

`[1, 2, 3]`

`>>> id(alist)`

`145381412`

`>>> id(blist)`

`140406428`

`>>> id(clist)`

`140406428`

`>>> alist is blist    # Returns 1 if True, 0 if False`

`0`

`>>> blist is clist    # Ditto`

`1`

`>>> clist.append(4)   # Add an item to the end of the list`

`>>> clist`

`[1, 2, 3, 4]`

`>>> blist             # Same, because they both point to the same object`

`[1, 2, 3, 4]`

`>>> alist             # This one only looked the same initially`

`[1, 2, 3]`

屬性

我們已經看到對象擁有屬性,並且 dir() 函數會返回這些屬性的列表。但是,有時我們只想測試一個或多個屬性是否存在。如果對象具有我們正在考慮的屬性,那麼通常希望只檢索該屬性。這個任務可以由 hasattr() 和 getattr() 函數來完成,如本例所示:

清單 31. 具有一個屬性;獲得一個屬性
 
`>>> print hasattr.__doc__`

`hasattr(object, name) -> Boolean`

`Return whether the object has an attribute with the given name.`

`(This is done by calling getattr(object, name) and catching exceptions.)`

`>>> print getattr.__doc__`

`getattr(object, name[, default]) -> value`

`Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.`

`When a default argument is given, it is returned when the attribute doesn't`

`exist; without it, an exception is raised in that case.`

`>>> hasattr(id, '__doc__')`

`1`

`>>> print getattr(id, '__doc__')`

`id(object) -> integer`

`Return the identity of an object.  This is guaranteed to be unique among`

`simultaneously existing objects.  (Hint: it's the object's memory address.)`

可調用

可以調用表示潛在行爲(函數和方法)的對象。可以用 callable() 函數測試對象的可調用性:

清單 32. 您能爲我做些事情嗎?

`>>> print callable.__doc__`

`callable(object) -> Boolean`

`Return whether the object is callable (i.e., some kind of function).`

`Note that classes are callable, as are instances with a __call__() method.`

`>>> callable('a string')`

`0`

`>>> callable(dir)`

`1`

實例

在 type() 函數提供對象的類型時,還可以使用 isinstance() 函數測試對象,以確定它是否是某個特定類型或定製類的實例:

清單 33. 您是那些實例中的一個嗎?
`>>> print isinstance.__doc__`

`isinstance(object, class-or-type-or-tuple) -> Boolean`

`Return whether an object is an instance of a class or of a subclass thereof.`

`With a type as second argument, return whether that is the object's type.`

`The form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for`

`isinstance(x, A) or isinstance(x, B) or ... (etc.).`

`>>> isinstance(42, str)`

`0`

`>>> isinstance('a string', int)`

`0`

`>>> isinstance(42, int)`

`1`

`>>> isinstance('a string', str)`

`1`

子類

我們先前提到過,定製類的實例從該類繼承了屬性。在類這一級別,可以根據一個類來定義另一個類,同樣地,這個新類會按照層次化的方式繼承屬性。Python 甚至支持多重繼承,多重繼承意味着可以用多個父類來定義一個類,這個新類繼承了多個父類。 issubclass() 函數使我們可以查看一個類是不是繼承了另一個類:

清單 34. 您是我母親嗎?
`>>> print issubclass.__doc__`

`issubclass(C, B) -> Boolean`

`Return whether class C is a subclass (i.e., a derived class) of class B.`

`>>> class SuperHero(Person):   # SuperHero inherits from Person...`

`...     def intro(self):       # but with a new SuperHero intro`

`...         """Return an introduction."""`

`...         return "Hello, I'm SuperHero %s and I'm %s." % (self.name, self.age)`

`...`

`>>> issubclass(SuperHero, Person)`

`1`

`>>> issubclass(Person, SuperHero)`

`0`

`>>>`

檢查時間

讓我們將上一節中討論的幾種檢查技術結合起來。爲了做到這一點,要定義自己的函數 — interrogate() ,它打印有關傳遞給它的任何對象的各種信息。以下是代碼,後面是其用法的幾個示例:

清單 35. 誰也沒料到它

`>>> def interrogate(item):`

`...     """Print useful information about item."""`

`...     if hasattr(item, '__name__'):`

`...         print "NAME:    ", item.__name__`

`...     if hasattr(item, '__class__'):`

`...         print "CLASS:   ", item.__class__.__name__`

`...     print "ID:      ", id(item)`

`...     print "TYPE:    ", type(item)`

`...     print "VALUE:   ", repr(item)`

`...     print "CALLABLE:",`

`...     if callable(item):`

`...         print "Yes"`

`...     else:`

`...         print "No"`

`...     if hasattr(item, '__doc__'):`

`...         doc = getattr(item, '__doc__')`

`...     doc = doc.strip()   # Remove leading/trailing whitespace.`

`...     firstline = doc.split('\n')[0]`

`...     print "DOC:     ", firstline`

`...`

`>>> interrogate('a string')     # String object`

`CLASS:    str`

`ID:       141462040`

`TYPE:     <``type` `'str'>`

`VALUE:    'a string'`

`CALLABLE: No`

`DOC:      str(object) -> string`

`>>> interrogate(42)             # Integer object`

`CLASS:    int`

`ID:       135447416`

`TYPE:     <``type` `'int'>`

`VALUE:    42`

`CALLABLE: No`

`DOC:      int(x[, base]) -> integer`

`>>> interrogate(interrogate)    # User-defined function object`

`NAME:     interrogate`

`CLASS:    function`

`ID:       141444892`

`TYPE:     <``type` `'function'>`

`VALUE:    <``function` `interrogate at 0x86e471c>`

`CALLABLE: Yes`

`DOC:      Print useful information about item.`

正如您在最後一個示例中所看到的, interrogate() 函數甚至可以應用於它本身。您沒有再比它更具“自省性”的工具了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章