Python: Как получить информацию о классе из объекта 'frame'? - PullRequest
16 голосов
/ 05 февраля 2010

Возможно ли получить какую-либо информацию о классе из объекта кадра? Я знаю, как получить файл (frame.f_code.co_filename), функцию (frame.f_code.co_name) и номер строки (frame.f_lineno), но хотел бы иметь возможность также получить имя класса активного объекта экземпляр кадра (или None, если не в экземпляре).

Ответы [ 4 ]

18 голосов
/ 08 февраля 2010

Я не верю, что на уровне объекта фрейма есть какой-либо способ найти действительный объект функции python, который был вызван.

Однако, если ваш код основан на общем соглашении: присваивая имя экземпляру метода self, вы можете сделать следующее:

def get_class_from_frame(fr):
  import inspect
  args, _, _, value_dict = inspect.getargvalues(fr)
  # we check the first parameter for the frame function is
  # named 'self'
  if len(args) and args[0] == 'self':
    # in that case, 'self' will be referenced in value_dict
    instance = value_dict.get('self', None)
    if instance:
      # return its class
      return getattr(instance, '__class__', None)
  # return None otherwise
  return None

Если вы не хотите использовать getargvalues, вы можете напрямую использовать frame.f_locals вместо value_dict и frame.f_code.co_varnames[:frame.f_code.co_argcount] вместо args.

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

  • если функция, не относящаяся к методу, использует self в качестве имени первого параметра, то get_class_from_frame ошибочно вернет класс первого параметра.
  • это может вводить в заблуждение при работе с дескрипторами (будет возвращать класс дескриптора, а не фактического экземпляра, к которому осуществляется доступ).
  • @classmethod и @staticmethod не принимают параметр self и реализованы с помощью дескрипторов.
  • и, конечно, намного больше

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

7 голосов
/ 30 марта 2010

Это немного короче, но примерно так же. Возвращает None, если имя класса недоступно.

def get_class_name():
    f = sys._getframe(1)

    try:
        class_name = f.f_locals['self'].__class__.__name__
    except KeyError:
        class_name = None

    return class_name
3 голосов
/ 29 марта 2013

Я только что наткнулся на этот пост, так как столкнулся с той же проблемой. Однако я не считал метод «я» приемлемым решением по всем перечисленным причинам.

Следующий код демонстрирует другой подход: учитывая объект фрейма, он ищет в глобальных объектах объект с совпадающим именем элемента и блоком кода. Поиск вряд ли является исчерпывающим, поэтому возможно, что не все классы будут обнаружены, но мы должны найти те классы, которые мы ищем, потому что мы проверяем соответствующие коды.

Объектом кода является добавление имени функции к имени класса, если оно найдено:

def get_name( frame ):
  code = frame.f_code
  name = code.co_name
  for objname, obj in frame.f_globals.iteritems():
    try:
      assert obj.__dict__[name].func_code is code
    except Exception:
      pass
    else: # obj is the class that defines our method
      name = '%s.%s' % ( objname, name )
      break
  return name

Обратите внимание на использование __dict__ вместо getattr для предотвращения отлова производных классов.

Обратите внимание, что глобального поиска можно избежать, если self = frame.f_locals['self']; obj = self.__class__ дает совпадение или любое значение obj in self.__class__.__bases__ или глубже, поэтому, безусловно, есть место для оптимизации / гибридизации.

1 голос
/ 30 сентября 2016

Если метод является методом класса, класс будет первым аргументом. Это печатает тип первого аргумента, если он присутствует для каждого кадра стека вызова:

    def some_method(self):
        for f in inspect.getouterframes(inspect.currentframe() ):
            args, _,_, local_dict = inspect.getargvalues(f[0])
            if args: 
                first_arg = args[0]
                first_value = local_dict[first_arg]
                print(type(first_value).__name__) 
...