Python:如何从'frame'对象中获取类的信息?
有没有办法从一个框架对象中获取任何类的信息?我知道怎么获取文件名(frame.f_code.co_filename)、函数名(frame.f_code.co_name)和行号(frame.f_lineno),但我还想知道如何获取当前框架中活动对象实例的类名(如果没有实例的话就返回None)。
6 个回答
我刚看到这个帖子,因为我也遇到了同样的问题。我认为使用'self'方法并不是一个合适的解决方案,原因已经在之前的讨论中提到过了。
下面的代码展示了一种不同的做法:给定一个框架对象,它会在全局范围内搜索一个具有匹配成员名称和代码块的对象。这个搜索并不是特别全面,所以可能并不是所有的类都会被找到,但找到的类应该是我们想要的,因为我们会验证匹配的代码。
这段代码的目的是在找到的情况下,把函数名加上它的类名:
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__
或更深层次的内容,那么就可以避免全局搜索,所以这里还有优化和混合的空间。
这个代码稍微简短一些,但功能差不多。如果找不到类名,它会返回 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
我不认为在框架对象层面上,有什么方法可以找到实际被调用的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
参数,并且是通过描述符实现的。- 还有很多其他问题
根据你想要做的具体事情,你可能需要花一些时间深入研究,找到这些问题的解决方法(你可以检查返回的类中是否存在框架函数,并且共享相同的源代码,检测描述符调用是可能的,类方法也是如此,等等)。