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 提示符( >>>
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()
函式甚至可以應用於它本身。您沒有再比它更具“自省性”的工具了。