在python

时间:2015-11-14 10:32:26

标签: python python-3.x caching functional-programming python-decorators

我首先要说的是,我怀疑这是一个可以通过函数式编程方法解决的解决方案,但我不太了解这些概念(但一直在尝试)。

我的目前解决方案基于:

为了学习(这就是所有这些!)如何构建装饰器,我决定制作一个简单的缓存装饰器。

但是我陷入了一个循环,我试图封装我在一个类中包装的函数,但是每当我调用我已经包装的函数时,我在包装类中调用__call__,所以无限期地。

我想我可以在链装饰器之间有一个闭包嵌套,但我不知道如何在一个范围内收集所有变量。

我很欣赏我可以将所有参数都放在一个装饰器调用中,但我的目的是学习如何链接装饰器并在它们之间存储状态。

任何人都可以建议一种方式(或修改我的方式)在链式装饰器之间存储状态吗?

我的设计是:

# main.py
import http.client
from cache import cache

@cache.keys('domain', 'url')
@cache.lifetime(3600)
def make_http_request(domain,url='/'):
    conn = httplib.HTTPConnection(domain)
    conn.request("GET",url)
    return conn.getresponse()

if __name__ == '__main__':
    print(make_http_request('http://example.com/'))

使用cache.py看起来像

import hashlib
import os
import inspect

__author__ = 'drews'

def expand(path):
    return os.path.abspath(os.path.expanduser(path))

class CacheManager():
    """Decorator to take the result and store it in a a file. If the result is needed again, then the file result is returned"""

    def __init__(self, function, function_arg_name):
        self.lifetime = 3600
        self.cache_keys = None
        self.cache_path = '~/.decorator_cache/'

        self.f = function
        self.arg_names = function_arg_name

    def __call__(self, *args, **kwargs):
        if len(args) > 0:
            arg_names = self.arg_names
            if 'self' in arg_names:
                arg_names.remove('self')
            key_args = dict(zip(arg_names, args))
            key_args.update(kwargs)
        else:
            key_args = kwargs
        self._initialise(cache_path=expand(self.cache_path))
        key = self._build_key(key_args)
        if self.key_exists(key):
            result = self.get_key(key)
        else:
            result = self.f()
            self.set_key(key, result)
        return result

    def _build_key(self, key_elements):
        m = hashlib.md5()
        for key in self.cache_keys:
            m.update(key_elements[key].encode('utf-8'))
        return m.hexdigest()

    def _initialise(self, cache_path):
        def initialise_path(path):
            if not os.path.isdir(path):
                (head, tail) = os.path.split(path)
                if not os.path.isdir(head):
                    initialise_path(head)
                os.mkdir(path)

        initialise_path(cache_path)

    def key_exists(self, key):
        path = os.path.join(expand(self.cache_path), key)
        return os.path.exists(path)


class CacheDefinitionDecorator(object):
    def __init__(self, *args, **kwargs):
        self.d_args = args


class CacheKeyDefinitionDecorator(CacheDefinitionDecorator):
    def __call__(self, func, *args, **kwargs):
        if not isinstance(func, CacheManager):
            func = CacheManager(func,inspect.getargspec(func)[0])
        func.cache_keys = self.d_args
        return func


class CacheLifetimeDefintionDecorator(CacheDefinitionDecorator):
    def __call__(self, func, *args, **kwargs):
        if not isinstance(func, CacheManager):
            func = CacheManager(func,inspect.getargspec(func)[0])
        func.lifetime = self.d_args[0]
        return func

class CacheStruct(object):
    def __init__(self, **kwargs):
        for item in kwargs:
            setattr(self, item, kwargs[item])


cache = CacheStruct(
    keys=CacheKeyDefinitionDecorator,
    lifetime=CacheLifetimeDefintionDecorator
)

0 个答案:

没有答案