Python 自省指南

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'>

標識

先前我們說過,每個對象都有標識、類型和值。值得注意的是,可能有多個變量引用同一對象,同樣地,變量可以引用看起來相似(有相同的類型和值),但擁有截然不同標識的多個對象。當更改對象時(如將某一項添加到列表),這種關於對象標識的概念尤其重要,如在下面的示例中,blistclist 變量引用同一個列表對象。正如您在示例中所見,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() 函數甚至可以應用於它本身。您沒有再比它更具“自省性”的工具了。


原文地址:http://www.ibm.com/developerworks/cn/linux/l-pyint/index2.html

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