【问题标题】:Getting function parameters and variables names and values from decorated function从修饰函数中获取函数参数和变量名称和值
【发布时间】:2021-04-08 15:10:01
【问题描述】:

可以让 logger 装饰器打印被装饰函数的变量名和值

def logger(f):
  def inner(*args):
    f(*args)
    print(vars())
  return inner 

@logger
def add(a,b):
  c = 5
  d = a + b +c
  return d

add(1,2)

输出:

{'args': (1, 2)}

预期输出:

{'d': 8, 'c': 5, 'b': 2, 'a': 1}

【问题讨论】:

  • 没那么简单。您的 logger 函数必须到达 f 内部才能报告变量值;它们不在同一范围级别。也许您可以阅读一些有关如何进入运行时堆栈的信息。即使这样,您仍希望add 调用vars 以获取变量的运行时状态。该信息仅在 add 运行时存在。
  • 一直在谷歌搜索并没有真正找到任何东西,您有任何资源可以了解如何达到运行时堆栈?
  • 理论上这可能在 CPython 中通过修改字节码来创建一个新的函数对象来实现,但它会很混乱。问题是您只能从函数的堆栈帧中获取局部变量,但访问它的唯一方法是从函数本身内部,因此需要更改函数以保留堆栈帧的副本或否则就是它的局部变量的字典。
  • 很可能有一个替代你想做的事情 - 我猜你想要这个装饰器来调试功能,所以也许现有的调试器会做你想做的事,也许是交互式的像Python Tutor 这样的可视化工具会做你想做的事,或者pdb API 可以用来做你想做的事。

标签: python python-3.x python-decorators


【解决方案1】:

是的,您可以使用inspect 模块获取有关传递给函数的参数的信息,或者您可以从func.__code__.co_varnames 中读取函数的所有局部变量。

例如:

import inspect


def logger(f):
  def inner(*args):
    print(f'signature: {inspect.signature(f)}')
    print(f'variables: {f.__code__.co_varnames}')
    return f(*args)
  return inner 


@logger
def add(a,b):
  c = 5
  d = a + b +c
  return d

结果:

>>> add(4,5)
signature: (a, b)
variables: ('a', 'b', 'c', 'd')
14

没有办法获取内部变量的值,但是可以获取参数的值:

def logger(f):
  def inner(*args):
    print(f'signature: {inspect.signature(f).bind(*args)}')
    return f(*args)
  return inner 

结果:

>>> add(9,10)
signature: <BoundArguments (a=9, b=10)>
24

请注意,SignatureBoundArguments 都有更多信息,如果您深入研究它们,您可以获得更多信息。请参阅inspect module docs 了解更多信息。

【讨论】:

  • 这会打印变量的名称,但不会打印它们的值。
  • @kaya3L Python 导师正在实时单步调试代码,就像调试器一样。局部变量的状态通常不能在执行之前知道,也不能在执行之后知道,这是装饰器访问被装饰函数的唯一时间。我想装饰器可以像调试器一样运行并运行函数的代码本身是可能的,但对于记录函数的内部状态来说,这似乎有点过头了。
  • 在函数执行过程中局部变量可以取不同的值这一事实几乎说明这是不可能的;它只是表明您需要选择一个时间(例如,当函数返回时)并在那个时候输出它们的值。碰巧的是,如果您在函数完成后保留对堆栈帧的引用,则堆栈帧将保留函数完成时局部变量的值。
  • @kaya3:是的,为了回答这个问题,编写代码将是矫枉过正:)
  • @YefetL 是的,这是可能的,但您是否真的想引入一个额外的执行层来实现看似调试的目的。我会查看logging 模块并将日志记录添加到那些(可能很少)需要如此详细日志记录的函数中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
  • 2018-05-23
  • 2023-03-27
  • 1970-01-01
  • 2018-10-25
  • 2021-02-08
  • 1970-01-01
相关资源
最近更新 更多