Python实现指标运算 21 Jun 2017 Python实现指标运算 概述 指标运算,包含嵌套的运算,实现了diff,sum,limit,sort. 流程: 函数表达式解析 -> 运行解析后的表达式 -> 出运算结果 包括组件: 基于pyparsing的nested function call grammar.支持表达式和python的args和kwargs,参数类型只支持string和float 运算函数,所有运算函数接收iterator进行迭代运算,kwargs作为辅助参数. 运行函数,负责根据pyparsing解析出来的词法结构来调度运行具体的运算函数(递归调度),还有负责准备参数 sample code # -*- utf-8 -*- from pyparsing import ( Group, Literal, Word, ZeroOrMore, alphas, alphanums, Combine, nums, Forward, ParseResults ) """ gramner to represent a simple nested function call function call only support identifier and float number, other datastructs such as array not support yet. """ # suppress means ignore this literal when parse expression lparen = Literal("(").suppress() rparen = Literal(")").suppress() comma = Literal(",").suppress() assign = Literal('=').suppress() identifier = Word(alphas, alphanums + "_") number = Word(nums+'.').setParseAction(lambda t: float(t[0])) # kwarg in python kwidentifier = Group(identifier + assign + ( number | identifier )).setParseAction(lambda t: {t[0][0]: t[0][1]}) functor = identifier # allow expression to be used recursively expression = Forward() arg = number | expression | kwidentifier | identifier args = arg + ZeroOrMore(comma + arg) expression << Group(functor + Group(lparen + args + rparen)) #print expression.parseString("func_f(func_g(garg1, garg2), func_h(harg1, harg2), farg3, limit=30.54)") """ end of pyparsing grammer, now we get expression to parse string. """ """ context store variable, for execute to get """ a = [i for i in map(lambda x: x+3, xrange(10))] b = [i for i in map(lambda x: x+2, xrange(10))] c = [i for i in map(lambda x: x+1, xrange(10))] # use to store variable context = { 'a': a, 'b': b, 'c': c } """ define calculator functions """ """ function manager """ class func_manager(object): funcs = {} @classmethod def reg(cls, func_class): cls.funcs[func_class.__name__] = func_class @classmethod def get(cls, name): return cls.funcs.get(name, lambda x:x) """ functions defination """ # diff # a diff b: each item in a substract each one in b @func_manager.reg class diff_func(object): def __init__(self, iterator): self.__it = iterator def __call__(self): for item in self.__it: yield reduce(lambda x,y=0: x-y, item) # test # print [i for i in diff_func(zip(a,b,c))()] # print [i for i in diff_func(zip(diff_func(zip(a, b))(), c))()] # limit @func_manager.reg class limit_func(object): def __init__(self, iterator, limit=-1): self.__it = iterator self.__limit = int(limit) def __call__(self): if self.__limit > -1: for i in xrange(self.__limit): yield next(self.__it) else: yield next(self.__it) # test # print [i for i in limit_func(iter(a), 5)()] # sort @func_manager.reg class sort_func(object): def __init__(self, iterator, asc=1): self.__it = iterator self.__asc = int(asc) def __call__(self): items = sorted(self.__it, reverse=(not self.__asc)) for i in items: yield i # test # print [i for i in sort_func(limit_func(diff_func(iter(zip(a,b,c)))(), 5)())()] # sum @func_manager.reg class sum_func(object): def __init__(self, iterator): self.__it = iterator def __call__(self): yield sum(self.__it) """ execute expression parsed by pyparsing """ def execute(ep): # Group(func, Group(arg1, arg2, ...)) func_name = ep[0] func_args = ep[1] args = [] kwargs = {} # prepare arg and kwargs for arg in func_args: if isinstance(arg, ParseResults): # recusive execute if is another cal_func args.append(execute(arg)) elif isinstance(arg, dict): kwargs.update(arg) else: real_arg = context.get(arg) args.append(real_arg) arg = args[0] if len(args)>1: # if more than one args, convert them to iterator with zip arg = iter(zip(*args)) return func_manager.get(func_name)(arg, **kwargs)() """ Test """ def test(): print "context %r" % context exp = 'diff_func(diff_func(a,b),c)' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == map(lambda x: x[0]-x[1]-x[2], zip(a,b,c)) exp = 'sum_func(a)' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == [sum(a)] exp = 'limit_func(diff_func(diff_func(a,b),c),limit=5)' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == [0,-1,-2,-3,-4] exp = 'sum_func(limit_func(diff_func(diff_func(a,b),c),limit=5))' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == [-10] exp = 'sort_func(a, asc=0)' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == sorted(a, reverse=1) exp = 'sort_func(limit_func(diff_func(diff_func(a,b),c),limit=5),asc=1)' print exp exp_parsed = expression.parseString(exp) ret = [i for i in execute(exp_parsed[0])] print ret assert ret == [0,-1,-2,-3,-4][::-1] test() """ ontext {'a': [3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 'c': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'b': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]} diff_func(diff_func(a,b),c) [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] sum_func(a) [75] limit_func(diff_func(diff_func(a,b),c),limit=5) [0, -1, -2, -3, -4] sum_func(limit_func(diff_func(diff_func(a,b),c),limit=5)) [-10] sort_func(a, asc=0) [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] sort_func(limit_func(diff_func(diff_func(a,b),c),limit=5),asc=1) [-4, -3, -2, -1, 0] """ generator python metric calculator