【问题标题】:Python: How to retrieve class information from a 'frame' object?Python:如何从“框架”对象中检索类信息?
【发布时间】:2011-01-13 06:57:50
【问题描述】:

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

【问题讨论】:

    标签: python introspection


    【解决方案1】:

    你好,请原谅死灵;但是这些都没有对我有用,所以受到这里的一些 sn-ps 的启发,我制作了一个替代方案。这就是我设法为 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】:

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

      【讨论】:

        【解决方案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__ 或更深,则可以避免全局搜索,因此肯定有优化/混合的空间。

        【讨论】:

        • 另请注意,它不适用于在本地范围内声明的类(想想您在适当位置声明简单 SUT 的测试方法。)
        【解决方案4】:

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

        【讨论】:

        • 不错的答案!更好的是,您可以使用f.f_locals['__class__'].__name__,这也应该涵盖@classmethod@staticmethod 的情况。
        • @LucioPaiva 不起作用。 f_locals 中没有键 '__class__'
        【解决方案5】:

        我不相信,在框架对象级别,有任何方法可以找到已调用的实际 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 参数,而是通过描述符实现。
        • 当然还有更多

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

        【讨论】:

        • 感谢您的回答,克莱门特;它详细而周到。我仍然希望得到的不仅仅是一个回应!但如果没有,这个就足够了......
        • 好的,等够久了。这是一个很好的答案!我曾希望框架结构能提供更好的支持,但事实就是这样。再次感谢克莱门特!
        • 要将一个添加到列表中,请注意“self”可能是派生类的一个实例,这可能不是我们感兴趣的。
        猜你喜欢
        • 1970-01-01
        • 2011-01-13
        • 1970-01-01
        • 2010-10-14
        • 2018-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多