Руководство по интроспекции на Python

Как изучать объекты Python

Интроспекция предоставляет полезную информацию об объектах вашей программы. Python, динамический, объектно-ориентированный язык программирования, обеспечивает потрясающую поддержку интроспекции. В этой статье демонстрируются многие из его возможностей, начиная самыми простыми видами помощи и заканчивая более сложными формами изысканий.

Патрик О'Брайен, Программист, Orbtech

Патрик О'Брайен - программист Python, консультант и преподаватель. Он автор PyCrust и разработчик проекта PythonCard. Совсем недавно Патрик руководил группой PyPerSyst, которая переносила Prevayler на Python. Сейчас он продолжает вести этот проект, но для новой интересной области. За более подробной информацией о Патрике и его работе обращайтесь на Orbtech Web site, или пишите ему на pobrien@orbtech.com.



28.03.2007

Что такое интроспекция?

В повседневной жизни, интроспекция - это проявление самоанализа. Интроспекция основывается на изучении собственных мыслей, ощущений, мотивов и поступков. Великий философ Сократ провел большую часть своей жизни, занимаясь самоанализом и призывая своих сограждан-афинян следовать своему примеру. Он даже утверждал, что для него самого "непроанализированная жизнь не стоит существования" (за более подробной информацией о Сократе см. ссылки, приведенные вResources).

В случае программирования интроспекция означает возможность изучать что-либо, чтобы определить, что это такое, что оно умеет и что может делать. Интроспекция предоставляет программистам огромные гибкость и контроль. Если вы хоть раз работали с языками программирования, которые поддерживают интроспекцию, вы, возможно, испытываете схожие чувства: "непроанализированный объект не стоит воплощения".

В этой статье рассмотрены интроспективные возможности языка программирования Python. Питоновская поддержка интроспекции пронизывает язык вглубь и вширь. В действительности, Python трудно представить без его интроспективных возможностей. Прочитав эту статью, вы сможете с легкостью "проникнуть в самое сердце" своих объектов Python.

Мы начнем наше изучение Питоновской интроспекции в самом общем виде, в каком только возможно, а затем окунемся в более сложные технологии. Возможно, кто-нибудь даже заявит, что характеристики, которые являются нашей отправной точкой, не заслуживают звания "интроспективных". Согласимся, что, попадают ли они или нет под определение интроспекции, остается спорным моментом. Цель этой статьи и наша единственная задача - найти ответы на интересующие вопросы.

Давайте приступим к нашему исследованию, используя Python интерактивно. При запуске Python из командной строки мы входим в оболочку Python, где можем вводить код Python и получать немедленный ответ от интерпретатора Python. (Команды, перечисленные в этой статье, выполнятся надлежащим образом для Python 2.2.2. Возможно, вы получите другие результаты и ошибки, если используете более раннюю версию. Последнюю версию Python можно скачать с Web-сайта Python (см. Resources.)

Листинг 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 предлагает напечатать 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>

Похоже, что мы немного продвинулись. Давайте введем 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(), мы увидели приветствие и некоторые указания, а затем приглашение справки. В приглашении мы ввели keywords и получили список ключевых слов Python. Получив ответ на свой вопрос, мы вышли из справочной утилиты, увидев короткое прощальное сообщение, и вернулись к приглашению Python.

Как видно из этого примера, интерактивная справочная утилита Python отображает информацию на различные темы или об отдельном объекте. Эта справочная утилита довольно удобна и действительно использует интроспективные возможности Python. Однако буквальное использование справки не показывает, как она получает информацию. А поскольку задача этой статьи - раскрытие всех интроспективных секретов Python, нам необходимо незамедлительно выйти за рамки справочной утилиты.

Прежде чем выйти из справки, давайте воспользуемся ею, чтобы получить список доступных модулей. Модули - это просто текстовые файлы, которые содержат код Python и имена которых заканчиваются на .py. Если мы напечатаем в приглашении Pythonhelp('modules') или введем 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

Один из модулей, предоставляющих внутреннюю информацию о самом Python, - это модуль sys. Вы используете модуль, импортируя его и ссылаясь на его содержимое (как, например, переменные, функции и классы) с помощью нотации точка (.). Модуль 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, как, например, PyCrust (за более подробной информацией о PyCrust см. ссылку, приведенную в Resources), то увидим что-нибудь вроде этого:

Листинг 11. Атрибут sys.argv при использовании PyCrust
>>> sys.argv[0]
'/home/pobrien/Code/PyCrust/PyCrustApp.py'

Переменная path - это путь поиска модуля, список каталогов, в которых Python будет искать модули во время импорта. Пустая строка, '', в первой позиции относится к текущему каталогу:

Листинг 12. Атрибут 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. Если мы напечатаем help('modules keywords') в приглашении Python, то увидим следующее:

Листинг 14. Запрашиваем справку о модулях с ключевыми словами
>>> 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 действительно создает список ключевых слов, явно доступных в виде атрибута kwlist модуля keyword. В модуле 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']

A как насчет модуля 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 распознает встроенные функции без нашего участия. И теперь мы видим это имя, __builtins__, возращенное обращением к dir() . Возможно, здесь имеется какая-то связь. Давайте введем имя __builtins__в приглашение Python и посмотрим, скажет ли нам Python о нем что-нибудь интересное:

Листинг 19. Что такое __builtins__?
>>> __builtins__
<module '__builtin__' (built-in)>

Итак, __builtins__, похоже, является в текущей области именем, связанным с объектом модуля по имени __builtins__. (Поскольку модули - это не простые объекты с одиночными значениями, 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__. Воспользуемся Питоновской директивой print, чтобы выходные данные было легче читать, поскольку строки документации часто содержат встроенные разделители строк (\n):

Листинг 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. Давайте рассмотрим строки документации для нашего класса 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

Мы упоминали слово "объект" уже несколько раз, но так и не дали ему определения. Объект в среде программирования во многом подобен объекту реального мира. Реальный объект имеет определенную форму, размер, вес и другие характеристики. Реальный объект может реагировать на свое окружение, взаимодействовать с другими объектами или выполнять задачу. Компьютерные объекты пытаются моделировать объекты, которые окружают нас в реальном мире, включая абстрактные объекты, как, например, документы, каталоги и бизнес-процессы.

Как и объекты реального мира, некоторые компьютерные объекты могут обладать общими характеристиками, допуская при этом небольшие вариации. Представьте книги, которые продаются в книжном магазине. У любого экземпляра книги может не хватать нескольких страниц, или они могут быть испачканы, и каждая копия книги может включать уникальный идентификационный номер. И, хотя каждая книга -уникальный объект, каждая книга с одним и тем же названием -лишь экземпляр оригинального шаблона; она сохраняет большинство характеристик оригинала.

Сказанное справедливо и в отношении объектно-ориентированных классов и экземпляров классов. Например, каждая строка Python обладает атрибутами, которые, как мы видели, были показаны функцией dir(). В приведенном выше примере мы определили наш собственный класс Person, который действует как шаблон для создания отдельных экземпляров Python, у каждого из которых свои собственные значения имени и возраста, но общая для всех возможность представиться. Это и есть объектно-ориентированное программирование.

На языке программирования объекты, следовательно - это нечто, что имеет тождественность и значение, а также определенный тип, обладает некими характеристиками и определенным поведением. Объекты наследуют многие из своих атрибутов от одного или более родительских классов. Кроме ключевых слов и специальных символов (подобных таким операторам, как +, -, *, **, /, %, <, > и т. д.) все на 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__':
    Сделать здесь что-нибудь уместное, наподобие вызова 
    # функции main(), определенной где-то в этом модуле.
    main()
else:
    # Ничего не делать. Этот модуль был импортирован другим 
    # модулем, который хочет воспользоваться этой функцией,
    # классом или другими полезными битами, которые он определил.

Type

Функция 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 само по себе награда. Все, что я прошу у своих друзей-Питонистов, это угощения за счет общественности.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=204573
ArticleTitle=Руководство по интроспекции на Python
publish-date=03282007