您可以使用
inspect.currentframe()获取调用框架对象,并通过其f_locals属性获取self绑定的对象:
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!
如果你有一个带有自变量的函数,它会产生意想不到的结果:
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"