Easy Python ASync。预编译器?

时间:2009-11-08 11:53:36

标签: python compiler-construction syntax asynchronous

想象你有一个像这样重要的功能:

def getMd5Sum(path):
    with open(path) as f:
        return md5(f.read()).hexdigest()

你认为Python足够灵活,允许这样的代码(注意$):

def someGuiCallback(filebutton):
    ...
    path = filebutton.getPath()
    md5sum = $getMd5Sum()
    showNotification("Md5Sum of file: %s" % md5sum)
    ...

要执行这样的事情:

def someGuiCallback_1(filebutton):
    ...
    path = filebutton.getPath()
    Thread(target=someGuiCallback_2, args=(path,)).start()

def someGuiCallback_2(path):
    md5sum = getMd5Sum(path)
    glib.idle_add(someGuiCallback_3, md5sum)

def someGuiCallback_3(md5sum):
    showNotification("Md5Sum of file: %s" % md5sum)
    ...

(glib.idle_add只是将一个函数推送到主线程的队列中)

我考虑过使用装饰器,但他们不允许我在调用后访问函数的“内容”。 (showNotification部分)

我想我可以编写一个'编译器'来在执行前更改代码,但它并不像最佳解决方案那样。

关于如何做上述事情,你有什么想法吗?

3 个答案:

答案 0 :(得分:2)

您可以使用导入挂钩来实现此目标......

...但我个人认为它有点讨厌。

如果你想沿着这条路走下去,基本上你要做的就是:

  • 您为扩展程序添加导入挂钩(例如“.thpy”)
  • 然后导入钩子负责(基本上)导入一些有效的代码作为导入的结果。
  • 该有效代码被赋予与您正在导入的文件有效相关的参数。
  • 这意味着您的预编译器可以在路上执行您喜欢的任何转换。

缺点:

  • 虽然以这种方式使用导入钩子会起作用,但它会让任何维护者或代码都感到惊讶。 (坏主意IMO)
  • 你这样做的方式依赖于imputil - 已经在python 3.0中删除了,这意味着你用这种方式编写的代码的生命周期有限。

个人我不会去那里,但是如果你这样做,那么Python Magazine会出现一个问题,其中有一些细节,我会建议得到一个后面的问题来阅读它。 (由Paul McGuire撰写,2009年4月号,可能以PDF格式提供)。

特别是使用imputil和pyparsing作为例子,但原理是相同的。

答案 1 :(得分:1)

这样的事情怎么样:

def performAsync(asyncFunc, notifyFunc):
    def threadProc():
        retValue = asyncFunc()
        glib.idle_add(notifyFunc, retValue)
    Thread(target=threadProc).start()

def someGuiCallback(filebutton):
    path = filebutton.getPath()
    performAsync(
        lambda: getMd5Sum(path),
        lambda md5sum: showNotification("Md5Sum of file: %s" % md5sum)
    )

使用lambdas有点难看,但它比使用预编译器技巧更简单,可能更具可读性。

答案 2 :(得分:0)

当然,您可以从装饰器访问功能代码(已编译),反汇编和破解它。您甚至可以访问其定义的模块源并重新编译它。但我认为这不是必要的。下面是使用装饰生成器的示例,其中yield语句用作同步和异步部分之间的分隔符:

from threading import Thread
import hashlib

def async(gen):
    def func(*args, **kwargs):
        it = gen(*args, **kwargs)
        result = it.next()
        Thread(target=lambda: list(it)).start()
        return result
    return func

@async
def test(text):
    # synchronous part (empty in this example)
    yield # Use "yield value" if you need to return meaningful value
    # asynchronous part[s]
    digest = hashlib.md5(text).hexdigest()
    print digest