如何在执行函数后获取函数的局部值?

Ros*_*ron 7 python

假设我有一个类似的功能f(a, b, c=None).目的是调用函数f(*args, **kwargs),然后构造一组新的args和kwargs,这样:

  1. 如果函数有默认值,我应该能够获取它们的值.例如,如果我称之为f(1, 2),我应该能够获得元组(1, 2, None)和/或字典{'c': None}.
  2. 如果在函数内部修改了任何参数的值,则获取新值.例如,如果我调用它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模块。


Nik*_*s R 5

我今天偶然发现了这个非常需要并想分享我的解决方案。

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)