向 Python 添加宏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15669921/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:40:08  来源:igfitidea点击:

Adding Macros to Python

pythonmacropy

提问by Amelio Vazquez-Reina

I would like to invoke the following code in-situwherever I refer to MY_MACROin my code below.

我想在我在下面的代码提到的任何地方就地调用以下代码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()

Here is some code that would use MY_MACRO:

这是一些将使用的代码MY_MACRO

def some_function:
    MY_MACRO

def some_other_function:
    some_function()
    MY_MACRO

class some_class:
  def some_method:
     MY_MACRO

In case it helps:

如果有帮助:

  1. One of the reasons why I would like to have this ability is because I would like to avoid repeating the code of MY_MACROwherever I need it. Having something short and easy would be very helpful.
  2. Another reason is because I want to embed an IPython shell wihthin the macro and I would like to have access to all variables in locals().items()(see this other question)
  1. 我想要拥有这种能力的原因之一是因为我想避免MY_MACRO在需要的地方重复代码。拥有简短而简单的东西会非常有帮助。
  2. 另一个原因是因为我想在宏中嵌入一个 IPython shell,并且我想访问其中的所有变量locals().items()(请参阅其他问题

Is this at all possible in Python? What would be the easiest way to get this to work?

这在 Python 中完全可能吗?让它发挥作用的最简单方法是什么?

Please NOTEthat the macro assumes access to the entire namespace of the scope in which it's called (i.e. merely placingthe code MY_MACROin a function would not work). Note also that if I place MY_MACROin a function, linenowould output the wrong line number.

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

采纳答案by Li Haoyi

MacroPyis a project of mine which brings syntactic macros to Python. The project is only 3 weeks old, but if you look at the link, you'll see we have a pretty cool collection of demos, and the functionality you want can definitely be implemented using it.

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

On the other hand, python has some pretty amazing introspection capabilities, so I suspect you may be able to accomplish what you want purely using that functionality.

另一方面,python 有一些非常惊人的内省能力,所以我怀疑你可能能够纯粹使用该功能来完成你想要的。

回答by Ned Batchelder

How about a function you can call? This function accesses the caller's frame, and rather than using locals(), uses frame.f_localsto get the caller's namespace.

你可以调用的函数怎么样?此函数访问调用者的框架,而不是使用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']

Then just call it:

然后只需调用它:

def some_function:
    my_function()

def some_other_function:
    some_function()
    my_function()

class some_class:
  def some_method:
     my_function()

回答by Serdalis

you could use function if you wanted to:

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

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

回答by tom

I'd say you should define a function to do this, since there are no macros in Python. It looks like you want to capture the current stack frame, which you could do simplify by passing in currentframe()from the call site to your shared function. Ditto with locals.

我想说你应该定义一个函数来做到这一点,因为 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())

回答by abarnert

I'm not sure if this is a good solution, but it's at least worth considering a macro preprocessor.

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

There are a few different extend-Python-with-macros projects, or wider projects that should make such a thing easier to do, but I only have expired links for all of them (Logix, MetaPython, Mython, Espy)… It might be worth looking for current links and/or newer/liver projects.

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

You can use something like m4or cpp, or something more powerful, or even build one yourself. But really, you've just got a small, static set (so far, one) of purely textual macros. At worst you have to detect the indentation level of MY_MACROand add that to the start of each line, which is trivial to do in a regex. Meaning sed, or a 3-liner Python script, can be your preprocessor.

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

However, there are two problems, or at least annoyances.

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

First, you need to preprocess your files. If you're already using C extension modules or generated code or any other code that needs you to setup.py(or makeor sconsor whatever) before you can run it, or you're using an IDE where you just hit cmd-R or ctrl-shift-B or whatever to test your code, this isn't a problem. But for the typical edit-test loop with a text editor in one window and an interactive interpreter in another…?well, you've just turned it into an edit-compile-test loop. Ugh. The only solution I can think of is an import hook that preprocesses every file before importing it as a module, which seems like a lot of work for a small benefit.

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

Second, your line numbers and source (from MY_MACROitself, as well as from tracebacks and inspect.getsourceand so on) are going to be the line numbers of the preprocessed files, not the original source that you have open for editing. Since your preprocessed files are pretty readable, that isn't terrible(not as bad as coding CoffeeScript and debugging it as JavaScript, which most of the CoffeeScript community does every day…), but it's definitely an annoyance.

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

Of course one way to solve this is to build your own macro processor into the interpreter, at whichever stage in the parse/compile process you want. I'm guessing that's a whole lot more work than you want to do, but if you do, well, Guido always prefers to have an actual working design and implementation to reject instead of having to keep rejecting vague suggestions of "Hey, let's add macros to Python". :)

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

回答by Dan

The use of execis frowned upon, but it ought to do the trick here. For example, take the following macro:

使用的exec就是令人难以接受的,但它应该在这里做的伎俩。例如,采用以下宏:

MY_MACRO = """
print foo            
"""

and run it by using the following code:

并使用以下代码运行它:

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

Always be careful with exec, because it can have strange side-effectsand opens up opportunities for code injection.

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

回答by Ahmad Yoosofan

If you need only line and function name of caller like I needed for debug, you can get caller function information by inspect.getouterframes link.

如果您只需要像我调试所需的调用者的行和函数名称,则可以通过 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()