2013-03-27 87 views
21

我想在下面的代码中引用以下代码原位无论我在何处引用MY_MACRO将宏添加到Python

# 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的外壳wihthin宏观和我想访问所有变量locals().items()(见this other question

在Python这是在所有可能的?什么是最简单的方法来实现它的工作?

注意,宏观假设访问范围的整个命名空间中,这就是所谓的(即仅仅将代码MY_MACRO功能将无法正常工作)。另请注意,如果我将MY_MACRO放入函数中,lineno会输出错误的行号。

+10

恩,你为什么不定义一个函数? – delnan 2013-03-27 21:32:47

+3

@delnan请阅读OP中的“NOTE”。 – 2013-03-27 21:46:46

+2

如果你想干涉框架,你可以去一个(或任何真正的数字)框架,以达到调用者的范围。这是假设你实际上有一个很好的理由来摆弄框架。还有跟踪功能(请参考文档),这可以减少侵入性。 – delnan 2013-03-27 21:56:34

回答

20

MacroPy是我的一个项目,它将语法宏引入Python。该项目只有3周的时间,但如果你看看链接,你会看到我们有一个非常酷的演示集合,并且你想要的功能可以绝对使用它来实现。另一方面,python有一些非常了不起的自省功能,所以我怀疑你可能能够完成你想要纯粹使用该功能的东西。

+4

MacroPy是否仍被支持?它看起来不错,只是我需要实现依赖关系跟踪 - 但它似乎不适用于Python 2.7.3,或者使用Python 3.4进行安装...... – 2016-09-14 12:44:48

13

如何调用一个函数?此函数访问调用者的框架,而不是使用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() 
+0

谢谢,请参阅我对@Serdalis的评论和OP中的NOTE。这个解决方案能解决他们吗 – 2013-03-27 21:39:41

+0

我修改了我的答案来解释一下。 – 2013-03-27 21:40:55

+0

谢谢奈德。这很棒。如果我打开一个嵌入式IPython shell,例如'MY_MACRO'中的''ipshell = InteractiveShellEmbed(config = cfg,banner1 = banner_msg,exit_msg = exit_msg)''(即我在'MY_MACRO'中调用'ipshell()')。我相信IPython不会看到来自调用者的变量。 (请参阅我的关于[此问题]的注释(http://stackoverflow.com/questions/15669186/using-ipython-as-an-effective-debugger))。任何想法如何解决这个问题? – 2013-03-27 21:45:06

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() 
+1

嗯,'current_state'是否保存了调用'MY_MACRO'的命名空间的变量?据我所知,'locals()'只会返回'macro_function()'的'locals'。我错了吗? – 2013-03-27 21:38:38

+0

@ user815423426对不起,误解了这个问题,现在已经修复了。 – Serdalis 2013-03-27 21:46:01

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()) 
1

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

有几个不同的扩展Python与宏项目,或更广泛的项目,应该使这样的事情更容易做,但我只有过期的链接(Logix,MetaPython,Mython,Espy) ...可能值得寻找当前的链接和/或更新/肝脏项目。

您可以使用类似m4cpp或更强大的东西,甚至可以自己创建一个。但是,真的,你刚刚得到了一个纯粹的文本宏的静态集合(迄今为止)。在最坏的情况下,您必须检测MY_MACRO的缩进级别,并将其添加到每行的开始处,这对于正则表达式来说是微不足道的。含义sed或3行Python脚本可以作为您的预处理器。

但是,有两个问题,至少是烦恼。

首先,您需要预处理您的文件。如果您已经在使用C扩展模块或生成的代码或任何其他需要您运行setup.py(或makescons或其他代码)的代码,或者您正在使用IDE,只需按下cmd-R或按Ctrl-Shift-B或其他来测试你的代码,这不是一个问题。但对于在一个窗口中使用文本编辑器并在另一个窗口中使用交互式解释器的典型编辑测试循环,您可以将其转换为编辑 - 编译测试循环。啊。我能想到的唯一解决方案是一个导入钩子,它在将每个文件作为模块导入之前对其进行预处理,这对于一个小好处来说似乎很多工作。其次,您的行号和来源(来自MY_MACRO本身,以及来自回溯和inspect.getsource等等)将成为预处理文件的行号,而不是您打开进行编辑的原始来源。由于您的预处理文件非常易读,这不是糟糕的(不像编码CoffeeScript并将其调试为JavaScript,大多数CoffeeScript社区每天都在进行调试......),但这绝对是一个烦恼。

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

4

exec的使用令人不悦,但它应该在这里做的伎俩。例如,采取下面的宏:

MY_MACRO = """ 
print foo    
""" 

,并通过使用下面的代码运行:

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

务必小心exec,因为它可以有strange side-effects和代码注入打开了机会。

1

如果您只需要调试程序的线路和函数名称(如调试所需的),则可以通过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()