前言
前面幾篇部落格我都是通過python自省來分析程式碼並試圖得出結論。當然,僅僅通過自省能解決的問題有限,但遇到問題還是不自主的去用這個功能,覺得對於認識程式碼的含義還是有一定幫助的。而這些自省的知識我都是從python自省指南中學習的。所以,下文的內容基本參考自這份指南,看官可以直接轉跳,筆者只是希望通過部落格整理自己的思路,檢驗學習效果。
python自省是什麼?
在計算機程式設計中,自省是指程式語言檢查某些事物以確定它是什麼、有何屬性及能做什麼。
尋求幫助–help
開啟python的IDLE,就進入到了python直譯器中,python直譯器本身是被認為是一個主模組,然後在直譯器提示符>>>下輸入一些我們想了解的資訊,所以首先我們會先尋求幫助,所以輸入help,接著輸入help(),我們就進入了help utility,然後循著提示keywords,modules,以瞭解python的關鍵字以及python自帶的或者我們額外安裝和定義的模組,如果要退出,輸入`q`,然後回車。
如果我們想了解某個物件(python裡面所有物件都可以認為是物件),也可以求助也help(),不過要在括號裡輸入物件的名稱,格式help(object),例如help(print),鑑於物件的自省內容太多,有的只貼上出部分內容。
>>> help Type help() for interactive help, or help(object) for help about object. >>> help() Welcome to Python 3.6`s help utility! If this is your first time using Python, you should definitely check out the tutorial on the Internet at https://docs.python.org/3.6/tutorial/. 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". ... help> keywords Here is a list of the Python keywords. Enter any keyword to get more help. False def if raise None del import return True elif in try and else is while as except lambda with assert finally nonlocal yield break for not class from or continue global pass help> modules Please wait a moment while I gather a list of all available modules... PIL base64 idlelib runpy __future__ bdb idna runscript __main__ binascii idna_ssl sched _ast binhex imaplib scrolledlist _asyncio bisect imghdr search _bisect browser imp ... Enter any module name to get more help. Or, type "modules spam" to search for modules whose name or summary contain the string "spam". >>> help(`print`) Help on built-in function print in module builtins: print(...) print(value, ..., sep=` `, end=` `, file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.
sys模組
sys模組提供關於python本身的詳細內在資訊。
>>> import sys >>> sys.executable #sys.executable 變數,它包含到 Python 直譯器的路徑; `D:\Python36\pythonw.exe` >>> sys.platform #platform變數顯示直譯器所處的作業系統; `win32` >>> sys.version # ersion變數說明直譯器的版本; `3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)]` >>> sys.argv[0] #vargv變數是一個包含命令列引數的列表; `` #argv[0]是指令碼執行的路徑,當我們以互動方式執行 Python 時,這個值是空字串; >>> sys.path #path 變數是模組搜尋路徑,Python 在匯入期間將在其中的目錄列表中尋找模組。最前面的空字串 `` 是指當前目錄; [``, `D:\Python36\Lib\idlelib`, `D:\Python36\python36.zip`, `D:\Python36\DLLs`, `D:\Python36\lib`, `D:\Python36`, `D:\Python36\lib\site-packages`] #變數sys.stdin、sys.stdout和sys.stderr是類似於檔案的流物件,表示標準的UNIX概念:標準輸入、標準輸出和標準錯誤。 >>> sys.stdin #Python從sys.stdin獲取輸入(如,用於input中), <idlelib.run.PseudoInputFile object at 0x02343F50> >>> sys.stdout # 將輸出列印到sys.stdout。 <idlelib.run.PseudoOutputFile object at 0x02343F70> >>> sys.stderr <idlelib.run.PseudoOutputFile object at 0x02343F90> >>> ```一個標準資料輸入源是sys.stdin。當程式從標準輸入讀取時,你可通過輸入來提供文字,也可使用管道將標準輸入關聯到其他程式的標準輸出。你提供給print的文字出現在sys.stdout中,向input提供的提示資訊也出現在這裡。寫入到sys.stdout的資料通常出現在螢幕上,但可使用管道將其重定向到另一個程式的標準輸入。錯誤訊息(如棧跟蹤)被寫入到sys.stderr,但與寫入到sys.stdout的內容一樣,可對其進行重定向,例如:$ cat somefile.txt | python somescript.py | sort。可以認為,somescript.py從其sys.stdin中讀取資料(這些資料是somefile.txt寫入的),並將結果寫入到其sys.stdout(sort將從這裡獲取資料)。``` #somescript.py內容 import sys text = sys.stdin.read() words = text.split() wordcount = len(words) print(`Wordcount:`, wordcount) #somefile.txt內容 Your mother was a hamster and your father smelled of elderberries. cat somefile.txt | python somescript.py的結果如下: Wordcount: 11
dir()函式、__doc__文件字串
dir()函式返回傳遞給它的任何物件的屬性名稱經過排序的列表。如果不指定物件,則 dir() 返回當前作用域中的名稱。
>>> dir() [`__annotations__`, `__builtins__`, `__doc__`, `__loader__`, `__name__`, `__package__`, `__spec__`] >>> >>> dir(sys) [`__displayhook__`, `__doc__`, `__excepthook__`, `__interactivehook__`, `__loader__`, `__name__`, `__package__`, `__spec__`, `__stderr__`, `__stdin__`, `__stdout__`, `_clear_type_cache`, `_current_frames`, `_debugmallocstats`, `_enablelegacywindowsfsencoding`, `_getframe`, `_git`, `_home`, `_xoptions`, `api_version`, `argv`, `base_exec_prefix`, `base_prefix`, `builtin_module_names`, `byteorder`, `call_tracing`, `callstats`, `copyright`, ... #在 dir() 示例中,有一個屬性是 __doc__ 屬性。這個屬性是一個字串,它包含了描述物件的註釋,python稱之為文件字串或 docstring。文件字串通常包含嵌入的換行 ,如何要使其變得易讀,可以print出來 >>> sys.__doc__ "This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter. Dynamic objects: argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else ... >>> print(sys.__doc__) This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter. Dynamic objects: argv -- command line arguments; argv[0] is the script pathname if knownpath -- module search path; path[0] is the script directory, else ...
type()–檢查python物件
程式設計環境中的物件試圖是模擬現實世界中的物件。實際的物件有一定的形狀、大小、重量和其它特徵,還會對其環境進行響應、與其它物件互動或執行任務。計算機中的物件則通過象文件、日程表和業務過程這樣的抽象物件模擬現實物件。
類似於實際的物件,幾個計算機物件可能共享共同的特徵,同時保持它們自己相對較小的變異特徵。書籍是個抽象的概念,書店書籍的每個物理副本都可能有汙跡、幾張破損的書頁或唯一的標識號。但每本書都只是原始模板的例項,並保留了原始模板的大多數特徵。再比如說柏拉圖的理想模型,可以說每種餅乾都是來自於同一個模具。
對於物件導向的類和類例項也是如此。每個例項都是以類為模板,每個例項都有類的相同屬性,但每個不同例項又有某些不同的地方比如屬性值等,這就是物件導向。
type() 函式有助於我們確定物件是字串還是整數,或是其它型別的物件。它通過返回型別物件來做到這一點,可以將這個型別物件與 types 模組中定義的型別相比較:
>>> help(type) Help on class type in module builtins: class type(object) | type(object_or_name, bases, dict) | type(object) -> the object`s type | type(name, bases, dict) -> a new type | | Methods defined here: | | __call__(self, /, *args, **kwargs) | Call self as a function. | | __delattr__(self, name, /) | Implement delattr(self, name). | | __dir__(...) | __dir__() -> list | specialized __dir__ implementation for types | | __getattribute__(self, name, /) | Return getattr(self, name). | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | ... >>> >>> type.__doc__ "type(object_or_name, bases, dict) type(object) -> the object`s type type(name, bases, dict) -> a new type" >>> dir(type) [`__abstractmethods__`, `__base__`, `__bases__`, `__basicsize__`, `__call__`, `__class__`, `__delattr__`, `__dict__`, `__dictoffset__`, `__dir__`, `__doc__`, `__eq__`, `__flags__`, `__format__`, `__ge__`, `__getattribute__`, `__gt__`, `__hash__`, `__init__`, `__init_subclass__`, `__instancecheck__`, `__itemsize__`, `__le__`, `__lt__`, `__module__`, `__mro__`, `__name__`, `__ne__`, `__new__`, `__prepare__`, `__qualname__`, `__reduce__`, `__reduce_ex__`, `__repr__`, `__setattr__`, `__sizeof__`, `__str__`, `__subclasscheck__`, `__subclasses__`, `__subclasshook__`, `__text_signature__`, `__weakrefoffset__`, `mro`] >>> object <class `object`> >>> object.__doc__ `The most base type` >>> help(object) Help on class object in module builtins: class object | The most base type >>> >>> >>> help(`class`) Class definitions ***************** A class definition defines a class object (see section The standard type hierarchy): classdef ::= [decorators] "class" classname [inheritance] ":" suite inheritance ::= "(" [argument_list] ")" classname ::= identifier A class definition is an executable statement. The inheritance list usually gives a list of base classes (see Metaclasses for more advanced uses), so each item in the list should evaluate to a class object which allows subclassing. Classes without an inheritance list inherit, by default, from the base class "object"; hence, class Foo: pass is equivalent to class Foo(object): pass
物件的名稱–__name__、__main__
並非所有物件都有名稱,但那些有名稱的物件都將名稱儲存在其 name 屬性中。名稱是從物件而不是引用該物件的變數中派生的。模組擁有名稱,Python 直譯器本身被認為是頂級模組或主模組。當以互動的方式執行 Python 時,區域性 name 變數被賦予值 `main` 。同樣地,當從命令列執行 Python 模組,而不是將其匯入另一個模組時,其 name 屬性被賦予值 `main` ,而不是該模組的實際名稱。這樣,模組可以檢視其自身的 name 值來自行確定它們自己正被如何使用,是作為另一個程式的支援,還是作為從命令列執行的主應用程式。
>>> print.__name__ `print` >>> import __main__ >>> help(__main__) Help on module __main__: NAME __main__ DATA __annotations__ = {} FILE (built-in) >>> __main__ <module `__main__` (built-in)> >>> class A: pass >>> A.__name__ `A` >>> __name__ `__main__` >>> A.__main__ Traceback (most recent call last): File "<pyshell#19>", line 1, in <module> A.__main__ AttributeError: type object `A` has no attribute `__main__` >>> __main__ <module `__main__` (built-in)> >>> help(__main__) Help on module __main__: NAME __main__ CLASSES builtins.object A class A(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) DATA __annotations__ = {} FILE (built-in) >>> class B: pass >>> help(__main__) Help on module __main__: NAME __main__ CLASSES builtins.object A B class A(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) class B(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) DATA __annotations__ = {} FILE (built-in) >>> dir(__main__) [`A`, `B`, `__annotations__`, `__builtins__`, `__doc__`, `__loader__`, `__main__`, `__name__`, `__package__`, `__spec__`, `sys`] >>> import json >>> dir(__main__) [`A`, `B`, `__annotations__`, `__builtins__`, `__doc__`, `__loader__`, `__main__`, `__name__`, `__package__`, `__spec__`, `json`, `sys`]
在檢查類例項的時候,我們常常會見到類似於這樣的自省:<class `main.A`>,這裡的__main__是一個模組,定義了頂層變數,A是該模組的一個屬性,這個屬性是一個類,這個類是A,當我們用type去檢查A的例項a時,就有了<class `main.A`>,a例項屬於主模組下的A類的例項。
>>> class A: pass >>> type(A) <class `type`> >>> a=A() >>> type(a) <class `__main__.A`>
id–標識,地址
每個物件都有標識、型別和值。id表明變數所引用的物件,值得注意的是,可能有多個變數引用同一物件,同樣地,變數可以引用看起來相似(有相同的型別和值),但擁有截然不同標識的多個物件。當更改物件時(如將某一項新增到列表),這種關於物件標識的概念尤其重要,id() 函式給任何給定物件返回唯一的識別符號。
>>> help(id) Help on built-in function id in module builtins: id(obj, /) Return the identity of an object. This is guaranteed to be unique among simultaneously existing objects. (CPython uses the object`s memory address.) >>> id.__doc__ "Return the identity of an object. This is guaranteed to be unique among simultaneously existing objects. (CPython uses the object`s memory address.)" >>> type(id) <class `builtin_function_or_method`> >>> a=5 >>> b=a >>> id(a) 1664672592 >>> id(b) 1664672592 >>>
可呼叫、例項、子類
可以用 callable() 函式測試物件的可呼叫性;在 type() 函式提供物件的型別時,還可以使用 isinstance() 函式測試物件,以確定它是否是某個特定型別或定製類的例項;issubclass() 函式使我們可以檢視一個類是不是繼承了另一個類;
>>> help(callable) Help on built-in function callable in module builtins: callable(obj, /) Return whether the object is callable (i.e., some kind of function). Note that classes are callable, as are instances of classes with a __call__() method. >>> callable.__doc__ `Return whether the object is callable (i.e., some kind of function). Note that classes are callable, as are instances of classes with a __call__() method.` >>> type(callable) <class `builtin_function_or_method`> >>> callable(print) True >>> callable(`a`) False >>> help(isinstance) Help on built-in function isinstance in module builtins: isinstance(obj, class_or_tuple, /) Return whether an object is an instance of a class or of a subclass thereof. A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B) or ...`` etc. >>> isinstance.__doc__ `Return whether an object is an instance of a class or of a subclass thereof. A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B) or ...`` etc.` >>> type(callable) <class `builtin_function_or_method`> >>> callable(43, int) Traceback (most recent call last): File "<pyshell#35>", line 1, in <module> callable(43, int) TypeError: callable() takes exactly one argument (2 given) >>> type(isinstance) <class `builtin_function_or_method`> >>> isinstance(45, int) True >>> isinstance(`a`, int) False >>> isinstance(`a`, str) True >>> help(issubclass) Help on built-in function issubclass in module builtins: issubclass(cls, class_or_tuple, /) Return whether `cls` is a derived from another class or is the same class. A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to check against. This is equivalent to ``issubclass(x, A) or issubclass(x, B) or ...`` etc. >>> issubclass.__doc__ "Return whether `cls` is a derived from another class or is the same class. A tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to check against. This is equivalent to ``issubclass(x, A) or issubclass(x, B) or ...`` etc." >>> type(issubclass) <class `builtin_function_or_method`> >>> class A: pass >>> class B(A): pass >>> issubclass(B, A) True >>> class C: pass >>> issubclass(B, C) False >>>