python
February 22

Модуль inspect и самый частый код разработчика, который можно упростить до пары строк

Превратите сложные задачи в простые: узнайте, как модуль inspect помогает разработчикам отлаживать и лучше понимать чужой код, а также сокращать время, затрачиваемое на рутинные действия.

Начнем с описания самых простых и полезных функций модуля.

getmodule и getfile

getmodule и getfile позволяют получить имя модуля и файла, в которых определен импортированный объект:

import inspect
from fastai.vision.all import *

inspect.getmodule(L)
inspect.getfile(L)

function signature


Для получения описания параметров и значений по умолчанию исследуемой функции обратитесь к signature:

def f(a, b:list=[2]):
  pass

sig = inspect.signature(f)

for name, param in sig.parameters.items():
    print(f"Parameter: {name}")
    print(f"  Default: {param.default}")
    print(f"  Annotation: {param.annotation}")
    print(f"  Kind: {param.kind}")
    print()

frame


Пожалуй, самым полезным объектом модуля является фрейм/блок. Функция currentframe возвращает ссылку на текущий блок кода, у которого есть следующие важные атрибуты:

  • f_locals - словарь локальных переменных
  • f_globals - словарь глобальных переменных
  • f_back - позволяет обратиться к фрейму на уровень выше
  • f_code - объект кода, привязанный к фрейму, как правило, описывает функцию, в которой происходит выполнение блока. В свою очередь, имеет атрибут co_varnames, содержащий имена аргументов и локальных переменных;

Ниже демонстрируются эти свойства фреймов:

def f2(a=1):
  frame = inspect.currentframe()
  print(f'f2 local vars - {(frame.f_locals.items())}')
  print(f'f2 func vars and locals - {frame.f_code.co_varnames}')
  print(f'f1 func vars and locals - {frame.f_back.f_code.co_varnames}')
  print(f'f0 func vars and locals - {frame.f_back.f_back.f_code.co_varnames}')

def f1(b):
  f2()

def f0(c=2):
  f1(2)

f0()

store_attr

Представленные выше свойства можно использовать для автоматизации присвоения внутренним атрибутам значений переменных, передаваемых в конструкторе класса, вида:

self.a=a

...

Этот шаблонный код, пожалуй, писал каждый питонист хотя бы раз.

class C():

    def __init__(self, a, b):
        frame = inspect.currentframe()
        for k, v in frame.f_locals.items():
          setattr(self, k, v)

item = C(a=3, b=[1, 3])
item.a, item.b

Если делать то же через функцию, то понадобится обратиться к вышестоящему фрейму:

def copy_args():

    frame = inspect.currentframe().f_back
    code = frame.f_code
    args = code.co_varnames

    self = frame.f_locals[args[0]]
    for k, v in frame.f_locals.items():
      setattr(self, k, v)

class C():
    def __init__(self, a, b):
        copy_args()

item = C(a=3, b=[1, 3])
item.a, item.b

Схожий функционал заложен в функции store_attr из модуля fastcore.basics:

from fastcore.basics import store_attr

class C():
    def __init__(self, a, b):
        store_attr()

item = C(a=3, b=[1, 3])
item.a, item.b

stack

Еще посредством функции stack модуля inspect из произвольной строки можно получить информацию о стеке вызовов по аналогии с тем, который выводится при ошибке. Пусть у нас есть модуль:

После вызова f0 мы получим информацию о номере строки текущего фрейма (lineno), функции (function) и файле, в котором она находится (filename). Кроме того, ниже из стека выводится информация о служебных инструментах, которые используются collab-ом:

import sys
sys.path.append('/content/drive/MyDrive/Colab Notebooks')
from my_funcs.module import f0

f0()