quant.common.decorators 源代码

import os
from inspect import signature
from types import FunctionType
from functools import wraps, singledispatch, update_wrapper
import pandas as pd
from tables.exceptions import HDF5ExtError
from ..common.settings import DATA_PATH
from ..common.logging import Logger


[文档]class Localizer: """ 把DataFrame缓存到本地hdf5文件中。通过设置key和const_key参数,可以设定需要跟踪哪些参数。 Examples ======== 无参数 .. code-block:: python localilzer = Localizer('./.cache') @localizer.wrap('foo', const_key="foo") def foo(): return pd.DataFrame([[1,2,3], [3,4,5]]) 由于hdf5文件需要key来定位数据,因此,对于没有参数的函数,需要给装饰器提供const_key参数方便保存 跟踪参数 .. code-block:: python localilzer = Localizer('./.cache') @localizer.wrap('zeros', keys=["length"]) def zeros(length): return pd.DataFrame(np.random.zeros(length, length)) 通过设置keys=["length"],缓存器可以根据传入的参数不同区分缓存内容。 """ def __init__(self, path): """ Parameters ========== path: str 要缓存到的路径(文件夹) """ self.path = path
[文档] def wrap(self, filename, keys=None, const_key=None, format="fixed"): """ 装饰器,用来装饰要缓存结果的函数 Parameters ========== filename: str 缓存到的文件名(无需后缀名) keys: List[str] 需要跟踪的参数名 const_key: str 基础键名 format: {'fixed', 'table'} 详见pd.DataFrame.to_hdf :: See :func:`pd.DataFrame.to_hdf` """ if keys is None and const_key is None: raise ValueError("Either `keys` or `const_key` must not be None") filename = os.path.join(self.path, filename) if not filename.endswith(".h5"): filename += ".h5" if keys is None: keys = [] if isinstance(keys, str): keys = [keys] def true_wrapper(wrapped): @wraps(wrapped) def func(*args, **kwargs): sig = signature(wrapped) bounded = sig.bind(*args, **kwargs) bounded.apply_defaults() path = "/".join(str(bounded.arguments[key]) for key in keys) if keys is not None else "" if const_key: path = os.path.join(path, const_key) if not path: path = "data" try: data = pd.read_hdf(filename, path) except (KeyError, FileNotFoundError): data = wrapped(*args, **kwargs) try: data.to_hdf(filename, path, format=format) except HDF5ExtError as e: Logger.error("Can't write to HDF5. {}".format(e)) return data return func return true_wrapper
LOCALIZER = Localizer(DATA_PATH) @singledispatch def single_instance(wrapped): """ 被该装饰器装饰的函数只会被调用一次,其结果会被缓存(但不区分参数)成为全局的单例。 适合用来建立数据库连接等。 Examples ======== .. code-block:: python # 该函数返回的实例是全局唯一的 @single_instance def get_conn(): return sqlalchemy.create_engine(...) a = get_conn() b = get_conn() assert a is b .. code-block:: python # 该类只能实例化一次 @single_instance class A: pass a = A() b = A() assert a is b """ pass @single_instance.register(FunctionType) def _(wrapped): _result = None @wraps(wrapped) def func(*args, **kwargs): nonlocal _result if _result is None: _result = wrapped(*args, **kwargs) return _result return func @single_instance.register(type) def _(cls): _result = None old_method = cls.__new__ @wraps(old_method) def new_method(*args, **kwargs): nonlocal _result if _result is None: _result = old_method(*args, **kwargs) return _result cls.__new__ = new_method return cls def method_dispatch(func): """ single_dispatch for methods """ dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper