首页 > 解决方案 > 使用 Traceback 获取函数/类的调用者名称的可堆叠装饰器?

问题描述

所以,我搞砸了并创建了这个伪状态机应用程序排序模式https://github.com/rebelclause/python_legs/blob/master/init_subclass_example.py大大扩展了这个回溯方法:https://stackoverflow .com/a/1690751/7955763

import traceback # for callable name
from functools import wraps

def tracename(orig_func):
    @wraps(orig_func)
    def wrapper(*args, **kwargs):
        (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
        def_name = text[:text.find('=')].strip()
        print(def_name)
        return def_name
    return wrapper

现在,我意识到这可能不是制作装饰器的正确方法;毕竟,回溯必须立即跟随要获取其调用者名称的函数。无论如何,我试着知道这一点,但现在乐趣结束了。我不确定我将如何使用它(即使在我提供的框架中),但有人可以回答如何改进装饰器和捕获调用者姓名的代码,以便它可以作为装饰器工作在一堆装饰器中?也许如何?

编辑:在避免协程问题的同时添加了这个...

import traceback # for callable name
from functools import wraps

# this should make you laugh, or not
def tracename(orig_func):
    @wraps(orig_func)
    def wrapper(*args, **kwargs):
        (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
        def_name = text[:text.find('=')].strip()
#        print(def_name)
        return def_name
    return wrapper


class foo(object):
    ''' '''
    def __init__(self):
        pass

    @tracename
    def _goodbye(self):
        print("It's been a good run so far, but this decorator might be toast.")


print(foo()._goodbye()) # prints wrapper returned var def_name

foo()._goodbye() # sits and watches while we patiently wait?

# uncomment the print statement in the decorator, then re-run

# then comment out the decorator and run it

guess_who = foo()._goodbye()

print('Guess where def_name went :', guess_who) # would it freak you out if the comment printed, too?

标签: pythonstatedecoratorcallable

解决方案


您的实际问题似乎在此评论中:

您可以按原样使用它,而无需更改类方法上的回溯索引 (-2),并且按原样,它将返回调用者,但是由于我尚未深入研究的原因,它不会运行方法的代码。

它不运行该方法的代码的原因很简单:您不调用该方法。

通常,装饰器看起来像这样:

def deco(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        # possibly modify args, kwargs
        # do any other pre-call stuff
        result = func(*args, **kwargs)
        # possibly modify result
        # do any other post-call stuff
        return result
    return wrapped

您的装饰器缺少对 的调用func,并且它还返回用于调用函数的字符串,而不是返回值。

如果您希望它像普通装饰器一样工作,只需执行普通装饰器的操作即可:

def tracename(orig_func):
    @wraps(orig_func)
    def wrapper(*args, **kwargs):
        (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
        def_name = text[:text.find('=')].strip()
        print(def_name)
        return orig_func(*args, **kwargs)
    return wrapper

还值得注意的是,您的函数实际上并没有“捕获调用者姓名”。它捕获的是调用语句的文本,在第一个被截断=- 或者,如果没有=,最后一个字符被剥离:

>>> @tracename
>>> def spam(n):
...     return n
>>> spam(n=1) # will truncate the "=1)"
spam(n
>>> spam(1) # will truncate the last character
spam(1
>>> print(spam(1)) # will truncate the last character
print(spam(1)
>>> x = spam(1) # will truncate the "= spam(1)"
x 

这些例子,或者我能想到的任何其他例子,都没有包含调用者的名字。来电者姓名是function_name您得到并忽略的。

虽然真的,除非你需要与 Python 2.6 或其他东西兼容,否则你可能inspecttraceback这里使用更好:

>>> def tracecallername(func):
...     @wraps(func)
...     def wrapped(*args, **kwargs):
...         frame = inspect.stack()[1]
...         print(frame.function)
...         # frame.code_context has the statement if you want that for something
...         return func(*args, **kwargs)
...     return wrapped
>>> @tracecallername
... def spam(n):
...     return n
>>> def eggs():
...     print(spam(1))
>>> eggs()
eggs
1

同时,如果“调用者”是指方法接收者——调用该方法的self实例——有一种更简单的方法可以做到这一点,因为self在 Python 方法调用中总是显式地作为第一个参数传递:

def tracereceiver(func):
    @wraps(func)
    def wrapped(self, *args, **kwargs):
        print(self)
        return func(self, *args, **kwargs)
    return wrapped

推荐阅读