Python:如何从“框架”对象中检索类信息?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2203424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-04 00:03:39  来源:igfitidea点击:

Python: How to retrieve class information from a 'frame' object?

pythonintrospection

提问by Kevin Little

Is it possible to retrieve any class information from a frame object? I know how to get the file (frame.f_code.co_filename), function (frame.f_code.co_name) and line number (frame.f_lineno), but would like to be able to also get the name of the class of the active object instance of the frame (or None if not in an instance).

是否可以从框架对象中检索任何类信息?我知道如何获取文件 (frame.f_code.co_filename)、函数 (frame.f_code.co_name) 和行号 (frame.f_lineno),但希望也能够获取活动对象的类名框架的实例(如果不在实例中,则为 None )。

回答by Clément

I don't believe that, at the frame object level, there's any way to find the actual python function object that has been called.

我不相信,在框架对象级别,有任何方法可以找到已被调用的实际 python 函数对象。

However, if your code rely on the common convention : naming the instance parameter of a method self, then you could do the following :

但是,如果您的代码依赖于通用约定:命名方法的实例参数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

If you don't want to use getargvalues, you can use directly frame.f_localsinstead of value_dictand frame.f_code.co_varnames[:frame.f_code.co_argcount]instead of args.

如果不想使用getargvalues,可以直接使用frame.f_locals代替value_dictframe.f_code.co_varnames[:frame.f_code.co_argcount]代替args

Keep in mind that this is still only relying on convention, so it is not portable, and error-prone:

请记住,这仍然仅依赖于约定,因此它不可移植,并且容易出错:

  • if a non-method function use selfas first parameter name, then get_class_from_framewill wrongly return the class of the first parameter.
  • it can be misleading when working with descriptors (it will return the class of the descriptor, not of the actual instance being accessed).
  • @classmethodand @staticmethodwon't take a selfparameter and are implemented with descriptors.
  • and surely a lot more
  • 如果非方法函数self用作第一个参数名称,get_class_from_frame则将错误地返回第一个参数的类。
  • 使用描述符时可能会产生误导(它将返回描述符的类,而不是正在访问的实际实例的类)。
  • @classmethod并且@staticmethod不会接受self参数并使用描述符实现。
  • 当然还有更多

Depending on what exactly you want to do, you might want to take some time to dig deeper and find workarounds for all these issues (you could check the frame function exist in the returned class and share the same source, detecting descriptor calls is possible, same for class methods, etc..)

根据您究竟想要做什么,您可能需要花一些时间深入挖掘并找到所有这些问题的解决方法(您可以检查返回的类中是否存在 frame 函数并共享相同的源,检测描述符调用是可能的,类方法等也一样。)

回答by Ruslan

This is a bit shorter, but does about the same. Returns None if class name not available.

这有点短,但大致相同。如果类名不可用,则返回 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

回答by gertjan

I just came across this post as I was faced with the same problem. I did not consider the 'self' method an acceptable solution, however, for all the reasons already listed.

我刚刚遇到了这篇文章,因为我遇到了同样的问题。然而,出于已经列出的所有原因,我并不认为“自我”方法是一个可接受的解决方案。

The following code demonstrates a different approach: given a frame object it searches the globals for an object with matching member name and code block. The search is hardly exhaustive so it is possible that not all classes will be uncovered, but what classes are found should be the ones we are looking for because we verify matching codes.

下面的代码演示了一种不同的方法:给定一个框架对象,它在全局变量中搜索具有匹配成员名称和代码块的对象。搜索并不详尽,因此可能不会发现所有类,但找到的类应该是我们正在寻找的类,因为我们会验证匹配代码。

Object of the code is to prepend a function name with its class name, if found:

代码的目标是在函数名前面加上它的类名,如果找到的话:

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

Note the use of __dict__instead of getattrto prevent catching of derived classes.

请注意使用__dict__代替getattr来防止捕获派生类。

Note further that a global search can be avoided if self = frame.f_locals['self']; obj = self.__class__gives a match, or any obj in self.__class__.__bases__or deeper, so there is certainly room for optimization / hybridization.

进一步注意,如果self = frame.f_locals['self']; obj = self.__class__给出匹配或任何obj in self.__class__.__bases__或更深的匹配,则可以避免全局搜索,因此肯定有优化/混合的空间。

回答by gerardw

If a method is a class method, the class will be the first argument. This prints out the type of the first arg if present for each calling stack frame:

如果方法是类方法,则该类将是第一个参数。这将打印出每个调用堆栈帧的第一个 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__)