22

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

4

5 回答 5

26

我不相信,在框架对象级别,有任何方法可以找到已调用的实际 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_dictframe.f_code.co_varnames[:frame.f_code.co_argcount]代替args

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

  • 如果一个非方法函数self用作第一个参数名称,那么get_class_from_frame将错误地返回第一个参数的类。
  • 使用描述符时可能会产生误导(它将返回描述符的类,而不是正在访问的实际实例的类)。
  • @classmethod并且@staticmethod不会带self参数并使用描述符实现。
  • 当然还有更多

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

于 2010-02-08T10:07:51.847 回答
8

这有点短,但大致相同。如果类名不可用,则返回 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
于 2010-03-30T11:14:08.853 回答
6

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

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

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

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__,因此肯定有优化/混合的空间。

于 2013-03-29T13:49:04.747 回答
1

如果方法是类方法,则类将是第一个参数。如果每个调用堆栈帧存在,这将打印出第一个 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__) 
于 2016-09-30T20:18:51.103 回答
0

你好,原谅死灵;但是这些都不适合我,所以我在这里的一些片段的启发下做了一个替代方案。这就是我设法为 python 3.7 中的类方法做到这一点的方法:

import inspect;

def QLNAME(frame):

    code = frame.f_code;
    name = code.co_name;
    qual = None;

    for cls in ( obj for obj in frame.f_globals.values()  #
                     if  inspect.isclass(obj)             ):

        if hasattr(cls, name):

            member = getattr(cls, name);
            if not inspect.isfunction(member): continue;

            if member.__code__ == code:
                qual = member.__qualname__;
                break;

    return qual;

它的工作方式其实很简单,我们检查一下

  1. 对于框架全局范围内的类
  2. 如果此类有成员共享相关名称
  3. 该成员是否为函数
  4. 最后对比一下两个函数的代码

如果 Nº4 为真,那么我们就知道这cls是一个具有某些方法的类member,其代码与我们从框架中得到的代码相同。

一切都很好,但这并不完美。据我测试,它不适用于属性装饰的方法,而且我还不完全确定如何处理这种极端情况。但它在常规和静态方法上对我有用,这是我目前所需要的,非常公平。

但是请注意,这非常慢,尽管我认为这是不言而喻的。根据我自己的基准,每 100,000 次调用通常需要大约 2 到近 3 秒。这对于我当前的用例来说是可以接受的,但是如果您需要一次多次使用它,您可能需要先进行一些优化。我会把它交给你有能力的人(:

希望在这里搜索引擎绊倒的其他人可以找到一些有用的东西。

干杯。

于 2021-03-04T11:58:41.717 回答