提问人:Marc B. Hankin 提问时间:10/27/2023 更新时间:10/27/2023 访问量:66
在 python 函数内部,使用表示 func 对象的 var 来获取 func 的参数和提供的值
From inside a python function, use a var representing the func object, to get the func's parameters and the values supplied
问:
函数中代码的第一行 'func' 下面是一个 print 语句。它打印/生成元组和字典之间的以下交叉,(在第一个等号的右侧)包含所有 func 的参数以及这些参数的默认值:
str(inspect.signature(func)) = (a='my', b='dog', c='chews', d='bones')
我想修改“func”第一行的 print 语句(与下面的 print 语句 #2 不同):
- 因此,每次调用 'func' 时,print 语句都会打印参数的名称以及:[i] 提供的值(如果它们与参数的默认值不同),以及 [ii] 默认值(如果未提供其他值);和
- 通过将字符串“func”替换为表示函数对象的代码变量,其中函数对象是代码/变量所在的函数(在本例中为“func”),作为对象。为了说明这一点,假设 X = PSEUDO_CODE_REPRESENTING_THIS_FUNCTION_OBJECT,并且以下行是函数中的第一行代码:
print('str(inspect.signature(X) = ' + str(inspect.signature(X))
下面是函数 'func'(在 'func' 的第一行包含一个 print 语句),在 'func' 的定义之后,第二个 print 语句(在代码的最后一行)向 'func' 提供非默认值:
def func(a='my', b='dog', c='chews', d='bones'):
print('str(inspect.signature(func)) = ' + str(inspect.signature(func)))
return a + ' ' + b + ' ' + c + ' ' + d
print('Statement #2: ' + func(a='her', b='cat', c='drinks', d='milk'))
任何建议将不胜感激,最好附有链接或一些解释。
答:
你应该做什么
在正常情况下,你绝对应该使用装饰器来做到这一点,因为你试图做的事情是一个非常糟糕的主意,需要弄乱 Python 中更坚韧的部分,同时也是意大利面条代码的巅峰之作。
下面是一个执行上述操作的装饰器:
import inspect
def wrap(f):
def inner(*args, **kwargs):
print(f'str(inspect.getsignature({f.__name__})) =', inspect.signature(f)) # use __name__ to get its' name
return f(*args, **kwargs)
return inner
@wrap
def func(a='my', b='dog', c='chews', d='bones'):
return a + ' ' + b + ' ' + c + ' ' + d
然而,这并不好玩!
你不应该做什么
Python 使用帧来实现调用堆栈,并提供了一个方便的库来摆弄它们,你已经遇到过 - 检查
库。
inspect.currentframe
返回一个 FrameInfo
对象,该对象将为我们提供调用该对象的帧 - 在本例中为表示函数的帧。
然后,我们去掉该帧的属性,它是一个代码对象 - 每个函数背后的肉。但是,代码对象还不是函数,你要求一个。令人恼火的是,没有办法将默认参数从框架中提取出来。f_code
我们只需在调用堆栈中跳一个,并使用我们从中获得的名称来访问调用方的名称,就可以解决这个问题。这为我们提供了我们的功能。f_code.co_name
f_locals
下面是一个实现:
import inspect
def func(a='my', b='dog', c='chews', d='bones'):
frame = inspect.currentframe()
name = frame.f_code.co_name
previous_frame_locals = frame.f_back.f_locals
f = previous_frame_locals[name]
print(f'str(inspect.getsignature({name})) =', inspect.signature(func))
return a + ' ' + b + ' ' + c + ' ' + d
不过这很啰嗦,你想要一个表情。
import inspect
def func(a='my', b='dog', c='chews', d='bones'):
print(f'str(inspect.getsignature({(name := (frame := inspect.currentframe()).f_code.co_name)})) =', inspect.signature(frame.f_back.f_locals[name]))
return a + ' ' + b + ' ' + c + ' ' + d
问题是,这是完全不可读的。我们可以做一个辅助函数!
import inspect
def helper():
frame = inspect.currentframe().f_back # make an additional step back up the stack
name = frame.f_code.co_name
previous_frame_locals = frame.f_back.f_locals
f = previous_frame_locals[name]
print(f'str(inspect.getsignature({name})) =', inspect.signature(func))
def func(a='my', b='dog', c='chews', d='bones'):
helper()
return a + ' ' + b + ' ' + c + ' ' + d
这可能是最好的解决方案,即使它仍然是针对 Python 的完全犯罪。在代码中,只需使用上面的装饰器即可。
最后一点,您实际上并不需要获取当前帧 - 您可以通过引发错误并使用回溯来获取帧来执行恶作剧。如果我们做其他操作,它将为我们节省导入,这将很好。它也是不可读的,所以不要这样做。无论如何,出于教育目的,它在这里。inspect
inspect.signature
import inspect
def helper():
# get the frame
try:
raise # anything that raises an exception, like 1/0, will work.
except Exception as e:
traceback = e.__traceback__
helper_frame = traceback.tb_frame
frame = helper_frame.f_back # make an additional step back up the stack
name = frame.f_code.co_name
previous_frame_locals = frame.f_back.f_locals
f = previous_frame_locals[name]
print(f'str(inspect.getsignature({name})) =', inspect.signature(func))
也要小心清除帧 - 如果不这样做,它可能会创建参考周期,而内存泄漏是不好的juju。
所有这些都是特定于 CPython 的,并且会在 API 更新时中断,这是定期的。
只需使用装饰器即可。
评论