【问题标题】:Stackable decorator using a Traceback to obtain caller name of function/class?使用 Traceback 获取函数/类的调用者名称的可堆叠装饰器?
【发布时间】:2018-08-02 03:55:00
【问题描述】:

所以,我搞砸了并创建了这个伪状态机应用程序排序模式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?

【问题讨论】:

  • 我不确定你在这里问什么。但通常,装饰器的包装器函数将使用传入的 args(或它们的某些转换版本)在某处调用包装函数并返回其结果(可能已转换)。你的只是返回一些它从函数调用者的堆栈框架中提取的字符串,而没有调用它,所以很难看出你如何有效地用任何堆叠在它上面的东西来组合它。
  • 您可以按原样使用它,而无需更改类方法上的回溯索引 (-2),并且它会返回调用者,但是,出于某种原因,我没有深入研究,它不会运行该方法的代码。
  • 我写了一个答案,解释如何让它运行该方法的代码——但如果这是您的实际问题,请编辑问题以说明这一点,而不是将其留在评论中。同时,我仍然不知道您要在这里提取和打印什么,但我做了一些猜测并将其添加到答案中。

标签: python state decorator callable


【解决方案1】:

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

您可以按原样使用它,而无需更改类的方法上的回溯索引 (-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

【讨论】:

  • 您提出了许多优点,并通过所提供示例的清晰性导致了可能的解决方案。检查功能似乎可以消除歧义或抽象回溯,虽然我知道,但您的观点会有所帮助。 eggs()brown_toast = spam(2)print('over easy: ', brown_toast) # 'brown_toast', the label as the frame capture
  • 我的例子中的方法是用实例/自我调用的,re。 foo()._goodbye(), foo() 为方法提供“自我”。
猜你喜欢
  • 1970-01-01
  • 2021-10-31
  • 1970-01-01
  • 2020-12-18
  • 2022-12-10
  • 2011-06-20
  • 1970-01-01
  • 2018-10-25
  • 1970-01-01
相关资源
最近更新 更多