使用Traceback获得函数/类的调用者名称的可堆叠装饰器?

时间:2018-08-02 03:55:01

标签: python state decorator callable

因此,我搞砸了并创建了这种伪造的State-Machine应用排序模式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?

1 个答案:

答案 0 :(得分:0)

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

  

您可以按原样使用它,而无需在类的方法上更改回溯索引(-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兼容,否则在这里使用inspect比使用traceback更好:

>>> 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
相关问题