我知道如何获取调用者方法的名称(从这里:How to get the caller's method name in the called method?)
import sys
print sys._getframe().f_back.f_code.co_name
我想得到的是此方法所属的类名(假设它在类中)。 因此,如果:
def get_some_info():
print('Class name of caller:', XXX)
class Base:
def my_method(self):
get_some_info()
class A(Base):
pass
class B(Base):
pass
a = A()
b = B()
a.my_method()
b.my_method()
应返回:
... A
... B
我应该在xxx
中做什么?
我尝试过(使用_getframe
上的信息)做类似的事情:
sys._getframe().f_back.f_code.__self__
但这不起作用
更新:
我不能将类名传递给一个被调用的函数(否则会很容易,但是要感谢所有提出此解决方案的人!)
答案 0 :(得分:5)
您可以使用inspect.stack()
:
def get_some_info():
_stack = inspect.stack()[1]
print ('cls:', _stack[0].f_locals['self'].__class__.__name__, 'func:', _stack[3])
....
a = A()
b = B()
a.my_method()
b.my_method()
打印:
('cls:', 'A', 'func:', 'my_method')
('cls:', 'B', 'func:', 'my_method')
答案 1 :(得分:5)
您可以使用inspect.currentframe()
获取调用框架对象,并通过其self
属性获取f_locals
所绑定的对象:
import inspect
def get_some_info():
# get the call frame of the calling method
frame = inspect.currentframe().f_back
try:
# try to access the caller's "self"
try:
self_obj = frame.f_locals['self']
except KeyError:
return None
# get the class of the "self" and return its name
return type(self_obj).__name__
finally:
# make sure to clean up the frame at the end to avoid ref cycles
del frame
此方法的缺点是它依赖于第一个参数命名为“ self”。在某些情况下,我们使用不同的名称,例如在编写元类时:
class MyMeta(type):
def __call__(cls, *args, **kwargs):
get_some_info() # won't work!
如果您有一个带有self
变量的函数,它会产生意外的结果:
def not_a_method():
self = 3
print(get_some_info()) # output: int
我们可以解决这两个问题,但这需要大量工作。我们可以通过调用代码对象的co_varnames
属性来检查“ self”参数的名称。为了检查调用函数是否真的是类中定义的方法,我们可以遍历self
的MRO并尝试查找调用我们的方法。最终结果是这种怪异:
def get_some_info():
# get the call frame of the calling method
frame = inspect.currentframe().f_back
try:
# find the name of the first variable in the calling
# function - which is hopefully the "self"
codeobj = frame.f_code
try:
self_name = codeobj.co_varnames[0]
except IndexError:
return None
# try to access the caller's "self"
try:
self_obj = frame.f_locals[self_name]
except KeyError:
return None
# check if the calling function is really a method
self_type = type(self_obj)
func_name = codeobj.co_name
# iterate through all classes in the MRO
for cls in self_type.__mro__:
# see if this class has a method with the name
# we're looking for
try:
method = vars(cls)[func_name]
except KeyError:
continue
# unwrap the method just in case there are any decorators
try:
method = inspect.unwrap(method)
except ValueError:
pass
# see if this is the method that called us
if getattr(method, '__code__', None) is codeobj:
return self_type.__name__
# if we didn't find a matching method, return None
return None
finally:
# make sure to clean up the frame at the end to avoid ref cycles
del frame
这应该可以正确处理您扔给它的几乎所有东西:
class Base:
def my_method(whatever):
print(get_some_info())
@functools.lru_cache() # could be any properly implemented decorator
def my_decorated_method(foo):
print(get_some_info())
@classmethod
def my_class_method(cls):
print(get_some_info())
class A(Base):
pass
def not_a_method(self=3):
print(get_some_info())
A().my_method() # prints "A"
A().my_decorated_method() # prints "A"
A.my_class_method() # prints "None"
not_a_method() # prints "None"
print(get_some_info()) # prints "None"