繁体   English   中英

Python 宏:用例?

[英]Python Macros: Use Cases?

如果 Python 有一个类似于 Lisp/Scheme(类似于MetaPython )的宏工具,你会如何使用它?

如果您是 Lisp/Scheme 程序员,您将宏用于哪些用途(除了在 Python 中具有清晰语法并行的东西,例如 while 循环)?

我相信宏与Python的文化背道而驰。 Lisp中的宏允许大球接近 ; 您可以重新定义语言,使其更适合您的问题域。 相反,Pythonic代码使用Python最自然的内置功能来解决问题,而不是以一种在不同语言中更自然的方式解决问题。

宏本质上是unpythonic。

这是一个稍晚的答案,但MacroPy是我的一个新项目, 它将宏带入Python。 我们有一个非常重要的演示列表,所有这些都是需要宏来实现的用例,例如提供一种非常简洁的方式来声明类:

@case
class Point(x, y)

p = Point(1, 2)
print p.x   # 1
print p     # Point(1, 2)

MacroPy已被用于实现以下功能:

  • 案例类,Scala中易于使用的代数数据类型
  • 功能编程世界中的模式匹配
  • 尾调优化
  • Quasiquotes,一种操纵程序片段的快速方法
  • 字符串插值,许多语言的常见功能,以及Pyxl。
  • 跟踪和智能断言
  • PINQ到SQLAlchemy,来自C#的LINQ to SQL的克隆
  • 来自Scala和Groovy的Quick Lambdas,
  • Parser Combinators,受到Scala的启发。

查看链接页面以了解更多信息; 我想我可以自信地说,我们展示的用例远远超过了迄今为止在这个主题= D上任何人的建议

lisp宏的一些示例:

  • ITERATE是一个有趣且可扩展的循环工具
  • CL-YACC / FUCC是在编译时生成解析器的解析器生成器
  • CL-WHO允许使用静态和动态部分指定html文档
  • Parenscript是一个javascript代码生成器
  • 各种简单的代码包装器,例如错误处理程序(我有一个执行代码并显示GtkMessageDialog,如果发生未处理的错误的with-gtk-error-message-handler),执行程序(例如,给定代码,在不同的线程中执行它; I有一个主内线程宏,它在不同的线程中执行代码; PCall库使用宏来包装要同时执行的代码)
  • 具有宏的GUI构建器(例如,指定窗口小部件层次结构和窗口小部件的属性,并具有用于创建所有窗口小部件的宏生成代码)
  • 在编译期间使用外部资源的代码生成器。 例如,处理C头并生成FFI代码的宏或基于数据库模式生成类定义的宏
  • 声明性FFI。 例如,指定外部结构,函数,它们的参数类型和具有宏来生成相应的lisp结构,具有类型映射和编组代码的函数
  • 基于Continuations的Common Lisp Web框架使用宏将代码转换为CPS(延续传递样式)形式。

这是我遇到的一个真实世界的例子,它可以通过宏或真正的元编程支持来实现,但由于Python中缺少两者,因此必须使用CPython字节码操作:

http://www.aminus.net/dejavu/chrome/common/doc/2.0a/html/intro.html#cpython

这是使用常规宏和读取宏的组合在Common Lisp中解决问题的方法,以扩展语法(可以在没有后者的情况下完成,但不是前者):

http://clsql.b9.com/manual/csql-find.html

使用闭包和元编程在Smalltalk中解决了同样的问题(Smalltalk是少数实际上正确传递消息的单调度OO语言之一):

http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg02096.html

在这里,我尝试在Common Lisp中实现Smalltalk方法,这很好地说明了元编程如何在后者中得到很好的支持:

http://carcaddar.blogspot.com/2009/04/closure-oriented-metaprogramming-via.html

在lisp中,宏只是抽象思想的另一种方式。

这是用clojure编写的不完整的光线跟踪器的一个例子:

(defmacro per-pixel
  "Macro.
Excecutes body for every pixel. Binds i and j to the current pixel coord."
  [i j & body]
  `(dotimes [~i @width]
     (dotimes [~j @height]
       ~@body)))

如果你想用坐标(i,j)对每个像素做一些事情,比如说,如果我是偶数,则绘制一个黑色像素,你会写:

(per-pixel i,j
  (if (even? i)
    (draw-black i,j)))

如果没有宏,这是不可能的,因为@body可以表示内部的任何内容(每像素ij @body)

这样的东西在python中也是可能的。 你需要使用装饰器。 您无法使用lisp宏执行所有操作,但它们非常强大

看看这个装饰器教程: http//www.artima.com/weblogs/viewpost.jsp? thread = 240808

有一个邮件列表发布archive.org镜像 ),这很好地解释了这一点。 帖子是关于Perl的,但它也适用于Python。

我之前看到的一些用例包括创建类工厂或从生产代码中剥离日志语句。

另请参阅此问题: Pythonic宏语法

我不认为Python需要宏,因为它们对两件事有用:

  1. 为某事创建DSL或更有说服力的语法(Lisp LOOP宏是一个很好的例子)。 在这种情况下,Python哲学故意决定反对它。 如果有一些明确的表示法缺失,您可以随时要求PEP。

  2. 通过在编译时预先计算内容来加快速度。 Python不是以速度为导向,因此您总是可以使用函数。

我不是说宏是错的,只是因为它们不适合Python哲学。 你总是可以在没有太多代码重复的情况下使用它们,因为你有鸭子输入和操作符重载。

作为旁注,我更愿意看到Lisp在Python中的重启比宏。

我用它来包装yield ,使我能够构建更强大的发电机管道。

目前,可以通过PEP(Python Enhancement Proposal)将功能添加到Python的唯一方法是语言。 这可能很慢,并且在您希望向仅对您的用例有用的语言添加功能的情况下,它不会帮助您。

例如, 有一个PEP来添加一个do-while循环 这可能会添加到Python中,但PEP是在2003年创建的。我今天想编写do-while循环,如果Python有宏,我可以这样做。

同样,有一个PEP添加标记的中断并继续,但这被拒绝。 如果标记的break语句会使我的代码更清晰,我可以使用宏来实现它们。

除了PEP,我还想要一个unless宏。 而不是写:

if not is_superman():
    dodge_bullet()

我可以写:

unless is_superman():
    dodge_bullet()

我想要一个case宏(通常在Lisp中称为cond )。 而不是写:

if x == FOO:
    do_stuff_with_foos()
elif x == BAR:
    do_stuff_with_bars()
elif x == BAZ:
    do_stuff_with_bazs()

我可以写:

switch x:
   case FOO:
       do_stuff_with_foos()
   case BAR:
       do_stuff_with_bars()
   case BAZ:
       do_stuff_with_bazs()

这些将直接实现为宏。 更复杂,更实用的宏包括:

  • Ruby样式的字符串插值,例如"hello there {user}" (可能最好用作读者宏)
  • 模式匹配

目前,这些只是其他语言的功能。 使用宏,我可以将它们添加到Python。 我甚至可以编写包含示例实现的PEP。 (有些PEP已经这样做了,但是他们被迫修改了解释器本身的C源。)

Hy,为了我自己的用途,我创建了一个Python模块(Espy),它允许带有参数,循环和条件代码生成的宏定义:创建一个source.espy文件,然后启动相应的函数,然后生成source.py.

它允许语法如下:

macro repeat(arg1):
    for i in range(%arg1%):
        socket
    print "stop"
   ...
repeat(5):
    print "Hi everybody"
    print "See you soon"

相当于:

...
for i in range(5):
    print "Hi everybody"
    print "See you soon"
print "stop"

其他语法:

macro doit(arg1):
    for i in %arg1%:
        socket suit(arg2):
            socket
            print %arg2%
        socket check(arg3):
            if %arg2%==%arg3%:
                socket
...
#use
doit(range(10)):
    suit(result):
        result=i*i
    check(result,25):
        print "I knew that 5*5 == 25"

相当于:

for i in range(10):
    result=i*i
    print result
    if result==25:
        print "I knew that 5*5 == 25"

更多,Espy有两个功能:“宏为”和“宏如果”。 一个例子:

macro for v in [6,10,12,20,23]:
    macro if 7<%v%<22:
        True:
            print "At %v%, I'm awake."
        False:
            print "At %v%, I'm sleeping."

由Espy翻译成:

print "At 6, I'm sleeping."
print "At 10, I'm awake."
print "At 12, I'm awake."
print "At 20, I'm awake."
print "At 23, I'm sleeping."

完整的文档和免费下载可以在这里找到: http//elp.chronocv.fr

在很多情况下我使用这个模块。 它允许更结构化和更短的代码。 有了它,我从1000行espy代码生成了65000行清晰高效的python代码,用于新的国际象棋引擎项目(仍在进行中)。

如果Python可以在futur版本中包含宏,它会变得更加令人印象深刻。

阅读“The Lambda Papers”,这样你就可以找到为什么人们会根据宏来提出建议。

你应该从'AIM-353 Lambda:The Ultimate Imperative'开始,然后使用'AIM-443 Lambda:The Ultimate GOTO'。 两者都可以在这里找到:

http://library.readscheme.org/page1.html

好吧,我想代替

print >> sys.stderr, "abc"

来写

err "abc"

在一些脚本中有许多调试打印输出语句。

我可以

import sys
err = sys.stderr

接着

print >> err, "abc"

它更短,但仍然需要太多字符。

我想使用宏来启用python代码中的sql语句。 - 从table1中选择*

来自C世界,我真的很感激能够有效记录丰富的消息。 而不是写作

if logger.level > logger.DEBUG:
    logger.log('%s' % (an_expensive_call_that_gives_useful_info(),))

使用宏,人们可以做一些类似的事情

DEBUG('%s' % (an_expensive_call_that_gives_useful_info(),))

可能如果您想在运行时使用源代码(例如调试)(比如printf调试表达式的值及其名称,这样您就不必再写两次)。

我能想到在python中执行它的唯一方法是将字符串传递给eval。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM