Автор оригинала: Doug Hellmann.
Цель:
Модуль inspect предоставляет функции для самоанализа в реальном времени. объекты и их исходный код.
Модуль inspect
предоставляет функции для изучения живых объектов, включая модули, классы, экземпляры, функции и методы. Функции в этом модуле можно использовать для получения исходного исходного кода функции, просмотра аргументов метода в стеке и извлечения информации, полезной для создания документации библиотеки для исходного кода.
Пример модуля
В остальных примерах этого раздела используется файл этого примера, example.py
.
example.py
# This comment appears first # and spans 2 lines. # This comment does not show up in the output of getcomments(). """Sample file to serve as the basis for inspect examples. """ def module_level_function(arg1, arg2'default', *args, **kwargs): """This function is declared in the module.""" local_variable arg1 * 2 return local_variable class A(object): """The A class.""" def __init__(self, name): self.name name def get_name(self): "Returns the name of the instance." return self.name instance_of_a A('sample_instance') class B(A): """This is the B class. It is derived from A. """ # This method is not part of A. def do_something(self): """Does some work""" def get_name(self): "Overrides version from A" return 'B(' + self.name + ')'
Проверка модулей
Первый вид интроспекции исследует живые объекты, чтобы узнать о них. Используйте getmembers ()
, чтобы обнаружить атрибуты членов объекта. Типы возвращаемых элементов зависят от типа сканируемого объекта. Модули могут содержать классы и функции; классы могут содержать методы и атрибуты; и так далее.
Аргументы для getmembers ()
– это объект для сканирования (модуль, класс или экземпляр) и необязательная функция предиката, которая используется для фильтрации возвращаемых объектов. Возвращаемое значение – это список кортежей с двумя значениями: имя члена и тип члена. Модуль inspect
включает несколько таких функций-предикатов с такими именами, как ismodule ()
, isclass ()
и т. Д.
inspect_getmembers_module.py
import inspect import example for name, data in inspect.getmembers(example): if name.startswith('__'): continue print('{} : {!r}'.format(name, data))
В этом образце печатаются элементы модуля example
. У модулей есть несколько частных атрибутов, которые используются как часть реализации импорта, а также набор __builtins__
. Все они игнорируются в выводе для этого примера, потому что они фактически не являются частью модуля, а список длинный.
$ python3 inspect_getmembers_module.py A :B : instance_of_a : module_level_function :
Аргумент predicate
можно использовать для фильтрации типов возвращаемых объектов.
inspect_getmembers_module_class.py
import inspect import example for name, data in inspect.getmembers(example, inspect.isclass): print('{} : {!r}'.format(name, data))
Теперь в вывод включаются только классы.
$ python3 inspect_getmembers_module_class.py A :B :
Проверка классов
Классы сканируются с помощью getmembers ()
так же, как и модули, хотя типы членов различны.
inspect_getmembers_class.py
import inspect from pprint import pprint import example pprint(inspect.getmembers(example.A), width65)
Поскольку фильтрация не применяется, на выходе отображаются атрибуты, методы, слоты и другие члены класса.
$ python3 inspect_getmembers_class.py [('__class__',), ('__delattr__', ), ('__dict__', mappingproxy({'__dict__': , '__doc__': 'The A class.', '__init__': , '__module__': 'example', '__weakref__': , 'get_name': })), ('__dir__', ), ('__doc__', 'The A class.'), ('__eq__', ), ('__format__', ), ('__ge__', ), ('__getattribute__', ), ('__gt__', ), ('__hash__', ), ('__init__', ), ('__init_subclass__', ), ('__le__', ), ('__lt__', ), ('__module__', 'example'), ('__ne__', ), ('__new__', ), ('__reduce__', ), ('__reduce_ex__', ), ('__repr__', ), ('__setattr__', ), ('__sizeof__', ), ('__str__', ), ('__subclasshook__', ), ('__weakref__', ), ('get_name', )]
Чтобы найти методы класса, используйте предикат isfunction ()
. Предикат ismethod ()
распознает только связанные методы экземпляров.
inspect_getmembers_class_methods.py
import inspect from pprint import pprint import example pprint(inspect.getmembers(example.A, inspect.isfunction))
Теперь возвращаются только несвязанные методы.
$ python3 inspect_getmembers_class_methods.py [('__init__',), ('get_name', )]
Вывод для B
включает переопределение для get_name ()
, а также новый метод и унаследованный метод __init __ ()
, реализованный в А
.
inspect_getmembers_class_methods_b.py
import inspect from pprint import pprint import example pprint(inspect.getmembers(example.B, inspect.isfunction))
Методы, унаследованные от A
, такие как __init __ ()
, идентифицируются как методы B
.
$ python3 inspect_getmembers_class_methods_b.py [('__init__',), ('do_something', ), ('get_name', )]
Проверка экземпляров
Самоанализ экземпляров работает так же, как и другие объекты.
inspect_getmembers_instance.py
import inspect from pprint import pprint import example a example.A(name'inspect_getmembers') pprint(inspect.getmembers(a, inspect.ismethod))
Предикат ismethod ()
распознает два связанных метода из A
в примере экземпляра.
$ python3 inspect_getmembers_instance.py [('__init__',>), ('get_name', >)]
Строки документации
Строку документации для объекта можно получить с помощью getdoc ()
. Возвращаемое значение – атрибут __doc__
с табуляцией, расширенной до пробелов, и с равномерным отступом.
inspect_getdoc.py
import inspect import example print('B.__doc__:') print(example.B.__doc__) print() print('getdoc(B):') print(inspect.getdoc(example.B))
Вторая строка строки документации имеет отступ, когда она извлекается напрямую через атрибут, но перемещается в левое поле с помощью getdoc ()
.
$ python3 inspect_getdoc.py B.__doc__: This is the B class. It is derived from A. getdoc(B): This is the B class. It is derived from A.
В дополнение к фактической строке документации можно получить комментарии из исходного файла, в котором реализован объект, если источник доступен. Функция getcomments ()
просматривает источник объекта и находит комментарии в строках, предшествующих реализации.
inspect_getcomments_method.py
import inspect import example print(inspect.getcomments(example.B.do_something))
Возвращенные строки включают префикс комментария с удаленным префиксом пробела.
$ python3 inspect_getcomments_method.py # This method is not part of A.
Когда модуль передается в getcomments ()
, возвращаемое значение всегда является первым комментарием в модуле.
inspect_getcomments_module.py
import inspect import example print(inspect.getcomments(example))
Смежные строки из файла примера включаются как один комментарий, но как только появляется пустая строка, комментарий останавливается.
$ python3 inspect_getcomments_module.py # This comment appears first # and spans 2 lines.
Получение источника
Если для модуля доступен файл .py
, исходный исходный код для класса или метода можно получить с помощью getsource ()
и getsourcelines () .
inspect_getsource_class.py
import inspect import example print(inspect.getsource(example.A))
Когда класс передается, все методы этого класса включаются в вывод.
$ python3 inspect_getsource_class.py class A(object): """The A class.""" def __init__(self, name): self.name = name def get_name(self): "Returns the name of the instance." return self.name
Чтобы получить источник для одного метода, передайте ссылку на метод в getsource ()
.
inspect_getsource_method.py
import inspect import example print(inspect.getsource(example.A.get_name))
В этом случае сохраняется исходный уровень отступа.
$ python3 inspect_getsource_method.py def get_name(self): "Returns the name of the instance." return self.name
Используйте getsourcelines ()
вместо getsource ()
для получения строк исходного кода, разделенных на отдельные строки.
inspect_getsourcelines_method.py
import inspect import pprint import example pprint.pprint(inspect.getsourcelines(example.A.get_name))
Возвращаемое значение от getsourcelines ()
– это кортеж
, содержащий список строк (строк из исходного файла) и номер начальной строки в файле, где появляется исходный код.
$ python3 inspect_getsourcelines_method.py ([' def get_name(self):\n', ' "Returns the name of the instance."\n', ' return self.name\n'], 23)
Если исходный файл недоступен, getsource ()
и getsourcelines ()
вызывают IOError
.
Сигнатуры методов и функций
В дополнение к документации для функции или метода можно запросить полную спецификацию аргументов, которые принимает вызываемый объект, включая значения по умолчанию. Функция signature ()
возвращает экземпляр Signature
, содержащий информацию об аргументах функции.
inspect_signature_function.py
import inspect import example sig inspect.signature(example.module_level_function) print('module_level_function{}'.format(sig)) print('\nParameter details:') for name, param in sig.parameters.items(): if param.kind inspect.Parameter.POSITIONAL_ONLY: print(' {} (positional-only)'.format(name)) elif param.kind inspect.Parameter.POSITIONAL_OR_KEYWORD: if param.default inspect.Parameter.empty: print(' {}{!r}'.format(name, param.default)) else: print(' {}'.format(name)) elif param.kind inspect.Parameter.VAR_POSITIONAL: print(' *{}'.format(name)) elif param.kind inspect.Parameter.KEYWORD_ONLY: if param.default inspect.Parameter.empty: print(' {}{!r} (keyword-only)'.format( name, param.default)) else: print(' {} (keyword-only)'.format(name)) elif param.kind inspect.Parameter.VAR_KEYWORD: print(' **{}'.format(name))
Аргументы функции доступны через атрибут parameters
в Signature
. parameters
– это упорядоченный словарь, сопоставляющий имена параметров с экземплярами Parameter
, описывающими аргумент. В этом примере первый аргумент функции, arg1
, не имеет значения по умолчанию, а arg2
имеет.
$ python3 inspect_signature_function.py module_level_function(arg1,, *args, **kwargs) Parameter details: arg1 *args **kwargs
Подпись
для функции может использоваться декораторами или другими функциями для проверки входных данных, предоставления различных значений по умолчанию и т. Д. Однако создание подходящего универсального и многоразового декоратора проверки имеет одну особую проблему, потому что это может быть сложно. для сопоставления входящих аргументов с их именами для функций, которые принимают комбинацию именованных и позиционных аргументов. Методы bind ()
и bind_partial ()
предоставляют необходимую логику для обработки сопоставления. Они возвращают экземпляр BoundArguments
, заполненный аргументами, связанными с именами аргументов указанной функции.
inspect_signature_bind.py
import inspect import example sig inspect.signature(example.module_level_function) bound sig.bind( 'this is arg1', 'this is arg2', 'this is an extra positional argument', extra_named_arg'value', ) print('Arguments:') for name, value in bound.arguments.items(): print('{} = {!r}'.format(name, value)) print('\nCalling:') print(example.module_level_function(*bound.args, **bound.kwargs))
Экземпляр BoundArguments
имеет атрибуты args
и kwargs
, которые можно использовать для вызова функции с использованием синтаксиса для развертывания кортежа и словаря в стеке как аргументы.
$ python3 inspect_signature_bind.py Arguments: arg1 = 'this is arg1' arg2 = 'this is arg2' args = ('this is an extra positional argument',) kwargs = {'extra_named_arg': 'value'} Calling: this is arg1this is arg1
Если доступны только некоторые аргументы, bind_partial ()
все равно создаст экземпляр BoundArguments
. Его нельзя будет полностью использовать, пока не будут добавлены остальные аргументы.
inspect_signature_bind_partial.py
import inspect import example sig inspect.signature(example.module_level_function) partial sig.bind_partial( 'this is arg1', ) print('Without defaults:') for name, value in partial.arguments.items(): print('{} = {!r}'.format(name, value)) print('\nWith defaults:') partial.apply_defaults() for name, value in partial.arguments.items(): print('{} = {!r}'.format(name, value))
apply_defaults ()
добавит любые значения из значений параметра по умолчанию.
$ python3 inspect_signature_bind_partial.py Without defaults: arg1 = 'this is arg1' With defaults: arg1 = 'this is arg1' arg2 = 'default' args = () kwargs = {}
Иерархии классов
inspect
включает два метода для работы непосредственно с иерархиями классов. Первый, getclasstree ()
, создает древовидную структуру данных на основе заданных классов и их базовых классов. Каждый элемент в возвращаемом списке является либо кортежем с классом и его базовыми классами, либо другим списком, содержащим кортежи для подклассов.
inspect_getclasstree.py
import inspect import example class C(example.B): pass class D(C, example.A): pass def print_class_tree(tree, indent1): if isinstance(tree, list): for node in tree: print_class_tree(node, indent + 1) else: print(' ' * indent, tree[0].__name__) return if __name__ '__main__': print('A, B, C, D:') print_class_tree(inspect.getclasstree( [example.A, example.B, C, D]) )
Результатом этого примера является дерево наследования для классов A
, B
, C
и D
. D
появляется дважды, поскольку он наследуется как от C
, так и от A
.
$ python3 inspect_getclasstree.py A, B, C, D: object A D B C D
Если getclasstree ()
вызывается с параметром unique
, установленным на истинное значение, результат будет другим.
inspect_getclasstree_unique.py
import inspect import example from inspect_getclasstree import * print_class_tree(inspect.getclasstree( [example.A, example.B, C, D], uniqueTrue, ))
На этот раз D
появляется в выводе только один раз:
$ python3 inspect_getclasstree_unique.py object A B C D
Порядок разрешения метода
Другая функция для работы с иерархиями классов – это getmro ()
, которая возвращает кортеж
классов в том порядке, в котором они должны сканироваться при разрешении атрибута, который может быть унаследован от базовый класс с использованием порядка разрешения методов (MRO). Каждый класс в последовательности появляется только один раз.
inspect_getmro.py
import inspect import example class C(object): pass class C_First(C, example.B): pass class B_First(example.B, C): pass print('B_First:') for c in inspect.getmro(B_First): print(' {}'.format(c.__name__)) print() print('C_First:') for c in inspect.getmro(C_First): print(' {}'.format(c.__name__))
Этот вывод демонстрирует природу поиска MRO «сначала в глубину». Для B_First
A
также стоит перед C
в порядке поиска, поскольку B
является производным от A
.
$ python3 inspect_getmro.py B_First: B_First B A C object C_First: C_First C B A object
Стек и рамки
Помимо самоанализа объектов кода, inspect
включает функции для проверки среды выполнения во время выполнения программы. Большинство этих функций работают со стеком вызовов и работают с кадрами вызовов . Объекты фрейма содержат текущий контекст выполнения, включая ссылки на выполняемый код, выполняемую операцию, а также значения локальных и глобальных переменных. Обычно такая информация используется для построения трассировки при возникновении исключений. Это также может быть полезно для ведения журнала или при отладке программ, поскольку кадры стека могут быть опрошены, чтобы обнаружить значения аргументов, переданных в функции.
currentframe ()
возвращает фрейм наверху стека (для текущей функции).
inspect_currentframe.py
import inspect import pprint def recurse(limit, keyword'default', *, kwonly'must be named'): local_variable '.' * limit keyword 'changed value of argument' frame inspect.currentframe() print('line {} of {}'.format(frame.f_lineno, frame.f_code.co_filename)) print('locals:') pprint.pprint(frame.f_locals) print() if limit 0: return recurse(limit - 1) return local_variable if __name__ '__main__': recurse(2)
Значения аргументов для recurse ()
включены в словарь локальных переменных фрейма.
$ python3 inspect_currentframe.py line 14 of inspect_currentframe.py locals: {'frame': , 'keyword': 'changed value of argument', 'kwonly': 'must be named', 'limit': 2, 'local_variable': '..'} line 14 of inspect_currentframe.py locals: {'frame': , 'keyword': 'changed value of argument', 'kwonly': 'must be named', 'limit': 1, 'local_variable': '.'} line 14 of inspect_currentframe.py locals: {'frame': , 'keyword': 'changed value of argument', 'kwonly': 'must be named', 'limit': 0, 'local_variable': ''}
Используя stack ()
, также можно получить доступ ко всем кадрам стека от текущего кадра до первого вызывающего. Этот пример аналогичен показанному ранее, за исключением того, что он ожидает до конца рекурсии, чтобы напечатать информацию о стеке.
inspect_stack.py
import inspect import pprint def show_stack(): for level in inspect.stack(): print('{}[{}]\n -> {}'.format( level.frame.f_code.co_filename, level.lineno, level.code_context[level.index].strip(), )) pprint.pprint(level.frame.f_locals) print() def recurse(limit): local_variable '.' * limit if limit 0: show_stack() return recurse(limit - 1) return local_variable if __name__ '__main__': recurse(2)
Последняя часть вывода представляет основную программу за пределами функции recurse ()
.
$ python3 inspect_stack.py inspect_stack.py[11] -> for level in inspect.stack(): {'level':>,,, code_context=[' for level in inspect.stack():\n'], inspect_stack.py[24] -> show_stack() {'limit': 0, 'local_variable': ''} inspect_stack.py[26] -> recurse(limit - 1) {'limit': 1, 'local_variable': '.'} inspect_stack.py[26] -> recurse(limit - 1) {'limit': 2, 'local_variable': '..'} inspect_stack.py[31] -> recurse(2) {'__annotations__': {}, '__builtins__':, '__cached__': None, '__doc__': 'Inspecting the call stack.\n', '__file__': 'inspect_stack.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x101f9c080>, '__name__': '__main__', '__package__': None, '__spec__': None, 'inspect': , 'pprint': , 'recurse': , 'show_stack': }
Существуют и другие функции для построения списков фреймов в различных контекстах, например, при обработке исключения. Дополнительную информацию см. В документации по trace ()
, getouterframes ()
и getinnerframes ()
.
Интерфейс командной строки
Модуль inspect
также включает интерфейс командной строки для получения сведений об объектах без необходимости записывать вызовы в отдельной программе Python. Входные данные – это имя модуля и необязательный объект внутри модуля. По умолчанию выводится исходный код названного объекта. Использование аргумента --details
вызывает печать метаданных вместо источника.
$ python3 -m inspect -d example Target: example Origin: .../example.py Cached: .../__pycache__/example.cpython-36.pyc Loader: <_frozen_importlib_external.SourceFileLoader object at 0 x103e16fd0> $ python3 -m inspect -d example:A Target: example:A Origin: .../example.py Cached: .../__pycache__/example.cpython-36.pyc Line: 16 $ python3 -m inspect example:A.get_name def get_name(self): "Returns the name of the instance." return self.name
Смотрите также
- стандартная библиотека документации для проверки
- Заметки о переносе Python 2 на 3 для проверки
- Порядок разрешения методов Python 2.3 – документация по порядку разрешения методов C3, используемому в Python 2.3 и более поздних версиях.
- pyclbr – модуль
pyclbr
обеспечивает доступ к той же информации, что иinspect
, путем синтаксического анализа модуля без его импорта. - PEP 362 – объект сигнатуры функции