【问题标题】:Module seemingly reimported in memoized Python function模块似乎在 memoized Python 函数中重新导入
【发布时间】:2015-03-06 08:10:16
【问题描述】:

我正在编写一个 Python 命令行实用程序,该实用程序涉及将字符串转换为 TextBlob,它是自然语言处理模块的一部分。导入模块非常慢,在我的系统上大约 300 毫秒。为了快速,我创建了一个记忆函数,仅在第一次调用该函数时将文本转换为 TextBlob。重要的是,如果我在同一文本上运行我的脚本两次,我想避免重新导入 TextBlob 并重新计算 blob,而是从缓存中提取它。这一切都已完成并且工作正常,除了由于某种原因,该功能仍然很慢。事实上,它和以前一样慢。我认为这一定是因为模块被重新导入,即使函数被记忆并且导入语句发生在记忆函数内部。

这里的目标是修复以下代码,以便记忆运行尽可能快,因为不需要重新计算结果。

这是核心代码的一个最小示例:

@memoize
def make_blob(text):
     import textblob
     return textblob.TextBlob(text)


if __name__ == '__main__':
    make_blob("hello")

这是记忆装饰器:

import os
import shelve
import functools
import inspect


def memoize(f):
    """Cache results of computations on disk in a directory called 'cache'."""
    path_of_this_file = os.path.dirname(os.path.realpath(__file__))
    cache_dirname = os.path.join(path_of_this_file, "cache")

    if not os.path.isdir(cache_dirname):
        os.mkdir(cache_dirname)

    cache_filename = f.__module__ + "." + f.__name__
    cachepath = os.path.join(cache_dirname, cache_filename)

    try:
        cache = shelve.open(cachepath, protocol=2)
    except:
        print 'Could not open cache file %s, maybe name collision' % cachepath
        cache = None

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        argdict = {}

        # handle instance methods
        if hasattr(f, '__self__'):
            args = args[1:]

        tempargdict = inspect.getcallargs(f, *args, **kwargs)

        for k, v in tempargdict.iteritems():
            argdict[k] = v

        key = str(hash(frozenset(argdict.items())))

        try:
            return cache[key]
        except KeyError:
            value = f(*args, **kwargs)
            cache[key] = value
            cache.sync()
            return value
        except TypeError:
            call_to = f.__module__ + '.' + f.__name__
            print ['Warning: could not disk cache call to ',
                   '%s; it probably has unhashable args'] % (call_to)
            return f(*args, **kwargs)

    return wrapped

这是一个演示,说明记忆化当前不节省任何时间:

❯ time python test.py
python test.py  0.33s user 0.11s system 100% cpu 0.437 total

~/Desktop
❯ time python test.py
python test.py  0.33s user 0.11s system 100% cpu 0.436 total

即使函数被正确记忆,也会发生这种情况(放在记忆函数中的打印语句只在脚本第一次运行时给出输出)。

我已将所有内容放在 GitHub Gist 中以防万一。

【问题讨论】:

  • 您能否更改脚本的运行方式,以在多次转换中分摊导入时间成本?只是导入慢还是转换慢?
  • Python 只会导入一次模块,memoization 不会改变任何东西。
  • @NickT,是导入速度慢。
  • @stranac,你的意思是说模块只导入一次,或者每次运行脚本时导入一次?因为我在这里运行了两次脚本。
  • 运行脚本时一次。我没有注意到您正在尝试使用搁置保存缓存。

标签: python performance python-import memoization textblob


【解决方案1】:

不同的方法怎么样:

import pickle

CACHE_FILE = 'cache.pkl'

try:
    with open(CACHE_FILE) as pkl:
        obj = pickle.load(pkl)
except:
    import slowmodule
    obj = "something"
    with open(CACHE_FILE, 'w') as pkl:
        pickle.dump(obj, pkl)

print obj

这里我们缓存的是对象,而不是模块。请注意,如果您的缓存对象需要slowmodule,则此不会为您节省任何费用。所以在上面的例子中你会看到节省,因为"something" 是一个字符串并且不需要slowmodule 模块来理解它。但是如果你做了类似的事情

obj = slowmodule.Foo("bar")

unpickling 过程会自动导入慢模块,从而抵消缓存的任​​何好处。

因此,如果您可以将 textblob.TextBlob(text) 操作为在 unpickled 时不需要 textblob 模块的内容,那么您将看到使用这种方法节省的费用。

【讨论】:

    猜你喜欢
    • 2015-10-27
    • 1970-01-01
    • 2014-05-22
    • 2022-11-11
    • 2016-12-19
    • 2021-03-13
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    相关资源
    最近更新 更多