35

我想在下面的代码中引用的任何地方就地MY_MACRO调用以下代码。

# MY_MACRO
frameinfo = getframeinfo(currentframe())
msg = 'We are on file ' + frameinfo.filename + ' and line ' +  str(frameinfo.lineno)
# Assumes access to namespace and the variables in which `MY_MACRO` is called. 
current_state = locals().items()

这是一些将使用的代码MY_MACRO

def some_function:
    MY_MACRO

def some_other_function:
    some_function()
    MY_MACRO

class some_class:
  def some_method:
     MY_MACRO

如果有帮助:

  1. 我想拥有这种能力的原因之一是因为我想避免MY_MACRO在需要的地方重复代码。有一些简短而简单的东西会很有帮助。
  2. 另一个原因是因为我想在宏中嵌入一个 IPython shell,并且我想访问其中的所有变量locals().items()(请参阅另一个问题

这在 Python 中完全可能吗?让这个工作最简单的方法是什么?

注意,该宏假定可以访问调用它的作用域的整个命名空间(即仅将代码MY_MACRO放在函数中是行不通的)。另请注意,如果我放置MY_MACRO在一个函数中,lineno将输出错误的行号。

4

7 回答 7

27

MacroPy是我的一个项目,它为 Python 带来了语法宏。该项目只有 3 周的历史,但是如果您查看链接,您会发现我们有一个非常酷的演示集合,并且您想要的功能绝对可以使用它来实现。

另一方面,python 有一些非常惊人的自省功能,所以我怀疑你可以完全使用这些功能来完成你想要的。

于 2013-05-11T04:10:38.453 回答
17

你可以调用的函数怎么样?此函数访问调用者的框架,而不是 using locals(),用于frame.f_locals获取调用者的命名空间。

def my_function():
    frame = currentframe().f_back
    msg = 'We are on file {0.f_code.co_filename} and line {0.f_lineno}'.format(frame)
    current_state = frame.f_locals
    print current_state['some_variable']

然后调用它:

def some_function:
    my_function()

def some_other_function:
    some_function()
    my_function()

class some_class:
  def some_method:
     my_function()
于 2013-03-27T21:34:33.627 回答
5

不赞成使用 of exec,但在这里应该可以解决问题。例如,采用以下宏:

MY_MACRO = """
print foo            
"""

并使用以下代码运行它:

foo = "breaking the rules"
exec MY_MACRO in globals(),locals() 

始终要小心exec,因为它可能会产生奇怪的副作用并为代码注入提供机会。

于 2014-12-03T19:06:10.280 回答
2

我不确定这是否是一个好的解决方案,但至少值得考虑使用宏预处理器。

有一些不同的extend-Python-with-macros 项目,或者更广泛的项目应该让这样的事情更容易做,但我只有所有这些项目(Logix、MetaPython、Mython、Espy)的过期链接......它可能是值得寻找当前链接和/或更新/肝脏项目。

你可以使用or 之类的东西,m4或者cpp更强大的东西,甚至自己构建一个。但实际上,您只有一个小的、静态的(到目前为止,只有一个)纯文本宏。在最坏的情况下,您必须检测缩进级别MY_MACRO并将其添加到每行的开头,这在正则表达式中是微不足道的。含义sed或 3 行 Python 脚本可以作为您的预处理器。

然而,有两个问题,或者至少是烦恼。

首先,您需要预处理您的文件。如果您已经在使用 C 扩展模块或生成的代码或任何其他需要您setup.py(或makescons或其他)的代码才能运行它,或者您正在使用只需点击 cmd-R 或 ctrl-shift 的 IDE -B 或任何测试您的代码的东西,这不是问题。但是对于典型的编辑-测试循环,一个窗口中有一个文本编辑器,另一个窗口有一个交互式解释器……好吧,你刚刚把它变成了一个编辑-编译-测试循环。啊。我能想到的唯一解决方案是一个导入钩子,它在将每个文件作为模块导入之前对其进行预处理,这似乎需要做很多工作才能带来一点好处。

其次,您的行号和源(来自MY_MACRO其本身,以及来自回溯inspect.getsource等)将是预处理文件的行号,而不是您打开以进行编辑的原始源。由于您的预处理文件可读性很强,这并不可怕(不像编写 CoffeeScript 并像 JavaScript 那样调试它那么糟糕,大多数 CoffeeScript 社区每天都在这样做......),但这绝对是一个烦恼。

当然,解决此问题的一种方法是将您自己的宏处理器构建到解释器中,在您想要的解析/编译过程的任何阶段。我猜这比你想做的要多得多,但如果你这样做了,好吧,Guido 总是更喜欢有一个实际工作的设计和实现来拒绝,而不是不得不继续拒绝“嘿,让我们添加宏到 Python”。:)

于 2013-03-27T23:39:30.943 回答
2

如果您只需要调用者的行和函数名,就像我调试所需的那样,您可以通过 inspect.getouterframes link获取调用者函数信息。

import inspect
def printDebugInfo():
  (frame,filename,line_number,function_name, lines, 
    index) = inspect.getouterframes(inspect.currentframe())[1]
  print(filename, line_number, function_name, lines, index)

def f1():
  printDebugInfo()

if __name__=='__main__':
  f1()
于 2016-06-03T08:46:49.117 回答
1

如果你想,你可以使用函数:

def MY_MACRO():
    frame = currentframe()
    try:
        macro_caller_locals = frame.f_back.f_locals
        print(macro_caller_locals['a'])

    finally:
        del frame

def some_function:
    a = 1
    MY_MACRO()
于 2013-03-27T21:35:04.363 回答
0

我想说您应该定义一个函数来执行此操作,因为 Python 中没有宏。看起来您想要捕获当前的堆栈帧,您可以通过currentframe()从调用站点传递到您的共享函数来简化它。与当地人同上。

def print_frame_info(frameinfo, locals):
    msg = 'We are on file ' + frameinfo.filename + ' and line ' +  str(frameinfo.lineno)
    current_state = locals.items()

def some_other_function:
    some_function()
    print_frame_info(currentframe(), locals())
于 2013-03-27T21:39:19.493 回答