2025年6月5日 星期四 乙巳(蛇)年 三月初九 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

Python 自省指南

时间:12-14来源:作者:点击数:3
CDSY,CDSY.XYZ

什么是自省?

在日常生活中,自省(introspection)是一种自我检查行为。自省是指对某人自身思想、情绪、动机和行为的检查。伟大的哲学家苏格拉底将生命中的大部分时间用于自我检查,并鼓励他的雅典朋友们也这样做。他甚至对自己作出了这样的要求:“未经自省的生命不值得存在。”

在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。一旦您使用了支持自省的编程语言,就会产生类似这样的感觉:“未经检查的对象不值得实例化。”

本文介绍了 Python 编程语言的自省能力。整个 Python 语言对自省提供了深入而广泛的支持。实际上,很难想象假如 Python 语言没有其自省特性是什么样子。在读完本文时,您应该能够非常轻松地洞察到自己 Python 对象的“灵魂”。

在深入研究更高级的技术之前,我们尽可能用最普通的方式来研究 Python 自省。有些读者甚至可能会争论说:我们开始时所讨论的特性不应称之为“自省”。我们必须承认,它们是否属于自省的范畴还有待讨论。但从本文的主旨出发,我们所关心的只是找出有趣问题的答案。

现在让我们以交互方式使用 Python 来开始研究。当我们从命令行启动 Python 时,就进入了 Python shell,在这里可以输入 Python 代码,而且立刻会从 Python 解释器获得响应。(本文中列出的命令可以使用 Python 2.2.2 正确执行。如果您使用较早的版本,则可能产生不同的结果或发生错误。可以从 Python 网站下载最新版本。)

清单 1. 以交互方式启动 Python 解释器
  • $ 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.
  • >>>

在让 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 global or
  • assert else if pass
  • break except import print
  • class exec in raise
  • continue finally is return
  • def for lambda try
  • del from not while
  • 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() 函数甚至可以应用于它本身。您没有再比它更具“自省性”的工具了。

结束语

谁知道自省可以变得这么简单而又如此有价值?可是,我在结束时必须提出警告:不要将自省的结果误认为是万能的。有经验的 Python 程序员知道:他们不知道的东西总是比已知的更多,因此根本不可能是万能的。编程行为产生的问题多于答案。关于 Python 的唯一优点(正如我们今天在本文中所见)是它确实回答了人们的问题。至于我本人,觉得您不必因为我帮助您了解了这些 Python 必须提供的内容而酬谢我。用 Python 编程自有回报。我从爱好 Python 的同伴处获得的帮助也是不计报酬的。

CDSY,CDSY.XYZ
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐