假设我有一个类似的功能f(a, b, c=None).目的是调用函数f(*args, **kwargs),然后构造一组新的args和kwargs,这样:
f(1, 2),我应该能够获得元组(1, 2, None)和/或字典{'c': None}.f(1, 100000, 3)并且函数确实if b > 500: b = 5修改了局部变量,我应该能够获得元组(1, 5, 3).这里的目的是创建一个完成函数工作的装饰器.原始函数充当为实际执行设置数据的前导码,装饰器完成作业.
编辑:我正在添加一个我正在尝试做的例子.它是一个为其他类创建代理的模块.
class Spam(object):
"""A fictional class that we'll make a proxy for"""
def eggs(self, start, stop, step):
"""A fictional method"""
return range(start, stop, step)
class ProxyForSpam(clsproxy.Proxy):
proxy_for = Spam
@clsproxy.signature_preamble
def eggs(self, start, stop, step=1):
start = max(0, start)
stop = min(100, stop)
然后,我们将拥有:
ProxyForSpam().eggs(-10, 200) -> Spam().eggs(0, 100, 1)
ProxyForSpam().eggs(3, 4) -> Spam().eggs(3, 4, 1)
Chi*_*chi 10
有两种配方可在这里,一个需要外部库,另一个是只使用标准库。他们并没有完全按照您的意愿行事,因为他们实际上修改了正在执行的函数以获取它locals()而不是获取locals()执行后的函数,这是不可能的,因为在函数完成执行后本地堆栈不再存在。
另一种选择是查看调试器(例如WinPDB甚至pdb模块)的功能。我怀疑他们使用该inspect模块(可能与其他模块一起)来获取正在执行函数的框架并以locals()这种方式检索。
编辑:阅读标准库中的一些代码后,您要查看的文件可能是bdb.py,它应该位于 Python 标准库的其余部分所在的任何位置。具体看set_trace()和相关函数。这将使您了解 Python 调试器如何进入类。您甚至可以直接使用它。让框架通过set_trace()查看inspect模块。
我今天偶然发现了这个非常需要并想分享我的解决方案。
import sys
def call_function_get_frame(func, *args, **kwargs):
"""
Calls the function *func* with the specified arguments and keyword
arguments and snatches its local frame before it actually executes.
"""
frame = None
trace = sys.gettrace()
def snatch_locals(_frame, name, arg):
nonlocal frame
if frame is None and name == 'call':
frame = _frame
sys.settrace(trace)
return trace
sys.settrace(snatch_locals)
try:
result = func(*args, **kwargs)
finally:
sys.settrace(trace)
return frame, result
Run Code Online (Sandbox Code Playgroud)
这个想法是用来sys.trace()捕捉下一帧的'call'。在 CPython 3.6 上测试。
示例用法
import types
def namespace_decorator(func):
frame, result = call_function_get_frame(func)
try:
module = types.ModuleType(func.__name__)
module.__dict__.update(frame.f_locals)
return module
finally:
del frame
@namespace_decorator
def mynamespace():
eggs = 'spam'
class Bar:
def hello(self):
print("Hello, World!")
assert mynamespace.eggs == 'spam'
mynamespace.Bar().hello()
Run Code Online (Sandbox Code Playgroud)