ГлавнаяРегистрацияВход Приветствую Вас Гость | RSS
   
Меню сайта
Разделы новостей
mp3player
Главная » 2007 » Июль » 8 » Руководство по интроспекции на Python(Часть 3)
Руководство по интроспекции на Python(Часть 3)
14:48

Патрик О'Брайен

Перевод: Intersoft Lab

Строки документации
Возможно, вы заметили в наших многочисленных примерах с dir() один атрибут среди множества других - __doc__. Этот атрибут - строка, которая содержит комментарии, описывающие объект. Python называет ее строкой документации, или docstring, и вот, как она работает. Если первая директива определения модуля, класса, метода или функции - строка, то эта строка оказывается связанной с объектом в качестве его атрибута __doc__. Посмотрите, например, на строку документации для объекта __builtins__. Воспользуемся Питоновской директивой print, чтобы выходные данные было легче читать, поскольку строки документации часто содержат встроенные разделители строк (\n):
 

Листинг 24. Строка документации модуля
>>> print __builtins__.__doc__ # Модуль докстроки
Built-in functions, exceptions, and other objects.

Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.[24]

И вновь, Python поддерживает даже строки документации для классов и методов, которые определены интерактивно в оболочке Python. Давайте рассмотрим строки документации для нашего класса Person и его метода intro:
 

Листинг 25. Строки документации класса и метода
>>> Person.__doc__ # Докстрока класса
'Person class.'
>>> Person.intro.__doc__ # Докстрока метода класса
'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.[26]

Опрос объектов 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() # Функция dir()
['__builtins__', '__doc__', '__name__']
>>> directory = dir # Создать новую переменную
>>> directory() # Работает просто как первоначальный объект
['__builtins__', '__doc__', '__name__', 'directory']
>>> dir.__name__ # Как тебя зовут?
'dir'
>>> directory.__name__ # У меня такое же имя
'dir'
>>> __name__ # А теперь о чем-нибудь совершенно другом
'__main__'

Модули имеют имена, а сам интерпретатор Python считается модулем верхнего уровня, или основным модулем. Когда вы запускаете Python интерактивно, локальной переменной __name__ присваивается значение '__main__'. Подобным образом, когда вы выполняете модуль Python из командной строки, а не импортируете его в другой модуль, его атрибуту __name__ присваивается значение '__main__', а не действительное имя этого модуля. Так модули могут взглянуть на свое значение __name__, чтобы определиться, используются ли они в качестве поддержки для другой программы или как основное приложение, выполняемое из командной строки. Следующая идиома весьма распространена в модулях Python:
 

Листинг 28. Определяем: выполнение или импорт
if __name__ == '__main__':
# Сделать здесь что-нибудь уместное, наподобие вызова
# функции main(), определенной где-то в этом модуле.
main()
else:
# Ничего не делать. Этот модуль был импортирован другим
# модулем, который хочет воспользоваться этой функцией,
# классом или другими полезными битами, которые он определил.

Функция 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.[29]

>>> 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.)[30]
>>> 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 # Возвращает 1 если True, 0 если False
0
>>> blist is clist # Аналогично
1
>>> clist.append(4) # Добавить элемент в конец списка
>>> clist
[1, 2, 3, 4]
>>> blist # То же самое, поскольку они обе указывают на один и тот же объект
[1, 2, 3, 4]
>>> alist # А этот исходно только выглядел таким же [1, 2, 3]
[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.)[31a]
>>> 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.[31b]
>>> 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.)[31c]

Вызываемые структуры
Объекты, которые представляют потенциальное поведение (функции и методы), могут быть запущены, или вызваны. С помощью функции 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.[32]
>>> 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.).[33]
>>> 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.[34]
>>> class SuperHero(Person): # SuperHero наследуется из Person...
... def intro(self): # но с новым 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') # Строка
CLASS: str
ID: 141462040
TYPE: <type 'str'>
VALUE: 'a string'
CALLABLE: No
DOC: str(object) -> string
>>> interrogate(42) # Целое
CLASS: int
ID: 135447416
TYPE: <type 'int'>
VALUE: 42
CALLABLE: No
DOC: int(x[, base]) -> integer
>>> interrogate(interrogate) # Функция, определенная пользователем
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 само по себе награда. Все, что я прошу у своих друзей-Питонистов, это угощения за счет общественности.

 

Часть первая http://vvs.clan.su/news/2007-07-08-87
Часть вторая http://vvs.clan.su/news/2007-07-08-88
Категория: Работа с Python | Просмотров: 756 | Добавил: VVS | Рейтинг: 0.0/0 |
Всего комментариев: 0
Имя *:
Email *:
Код *:
Поиск
Форма входа
Наш опрос
Чего Вам не хватает на сайте?
Всего ответов: 21
Друзья сайта
Статистика
Возраст