Python: как получить информацию о классе из объекта 'frame'?
Можно ли получить любую информацию о классе из объекта фрейма? Я знаю, как получить файл (frame.f_code.co_filename), функцию (frame.f_code.co_name) и номер строки (frame.f_lineno), но хотелось бы также получить имя класса активного объекта экземпляр фрейма (или None, если нет в экземпляре).
Ответы
Ответ 1
Я не считаю, что на уровне объекта фрейма можно найти реальный объект функции 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
и реализуются с дескрипторами.
- и, безусловно, намного больше
В зависимости от того, что именно вы хотите сделать, вам может потребоваться некоторое время, чтобы углубиться и найти обходные пути для всех этих проблем (вы можете проверить, существует ли функция фрейма в возвращаемом классе и совместно использовать один и тот же источник, обнаруживая вызовы дескриптора возможно, одинаково для методов класса и т.д.)
Ответ 2
Это немного короче, но примерно то же самое. Возвращает Нет, если имя класса недоступно.
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
Я только что наткнулся на этот пост, так как я столкнулся с той же проблемой. Однако я не считал метод "я" приемлемым решением по всем причинам, которые уже перечислены.
Следующий код демонстрирует другой подход: при заданном объекте фрейма он ищет глобальные объекты для объекта с соответствующим именем члена и блоком кода. Поиск вряд ли является исчерпывающим, поэтому возможно, что не все классы будут раскрыты, но какие классы будут найдены, должны быть те, которые мы ищем, потому что мы проверяем совпадающие коды.
Объектом кода является добавление имени функции с его именем класса, если найдено:
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__
или глубже, поэтому существует определенная возможность для оптимизации/гибридизации.
Ответ 4
Если метод является методом класса, класс будет первым аргументом. Это печатает тип первого arg, если он присутствует для каждого кадра стека вызовов:
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__)