大多数pythonic方法以递归方式将函数应用于字符串值

时间:2016-06-09 00:10:03

标签: python recursion

问题: 我想知道最先进的方法是递归地将函数应用于对象中的字符串值,而不事先知道它的模式?优选地,以通用的方式使其成为用于其他类型操作的可重用组件。

上下文 我正在处理一个json字符串作为来自api请求的输入,使用json.loads()加载它,在应用验证之前,我想在对象中的任何字符串之前或之后去掉任何空格。我希望这段代码具有自适应性,以便架构中的更改不会破坏它。

当前解决方案:

def strip(obj):
    return obj.strip()

def recurse_into(obj, baseaction, basetype=str):
    if isinstance(obj, basetype):
        return baseaction(obj)
    elif isinstance(obj, list):
        return [recurse_into(o, baseaction, basetype) for o in obj]
    elif isinstance(obj, tuple):
        return tuple(recurse_into(o, baseaction, basetype) for o in obj)
    elif isinstance(obj, dict):
        return dict((k, recurse_into(v, baseaction, basetype)) 
                    for (k, v) in obj.items())
    else:
        return obj

def generate_recurse(baseaction, basetype=str):
    def f(obj):
        return recurse_into(obj, baseaction, basetype)
    return f

def recursive_strip_whitespace(obj):
    clean_whitespace = generate_recurse(strip)
    return clean_whitespace(obj)

当前解决方案的问题: 它看起来非常简洁,很难理解那些没有写它的人会发生什么,我真的希望有更可读的方法来做到这一点。或者这是否真的是最好的?

3 个答案:

答案 0 :(得分:0)

我的建议是减少一些冗余代码:

def strip(obj):
    return obj.strip()

def recurse_into(obj, baseaction, basetype=str):
    if isinstance(obj, basetype):
        return baseaction(obj)
    elif isinstance(obj, list):
        return [recurse_into(o, baseaction, basetype) for o in obj]
    elif isinstance(obj, tuple):
        return tuple(recurse_into(o, baseaction, basetype) for o in obj)
    elif isinstance(obj, dict):
        return dict((k, recurse_into(v, baseaction, basetype)) 
                    for (k, v) in obj.items())
    return obj

def recursive_strip_whitespace(obj):
    return recurse_into(obj, strip)

答案 1 :(得分:0)

同样的方法"逆转"将案例拆分为单独的函数并将其映射出来。它的变形性较小,但可能看起来更具可读性。

def strip_obj(obj):
    return obj.strip()
def strip_tuple(tuple):
    return tuple(recurse(obj) for obj in tupl)
...
def recurse(root):
    actions = {basetype: strip_obj,
              tuple: strip_tuple,
              ...}
    return actions[type(root)](root)

请注意,对于iterables,你可以"去功能"与map,但我个人觉得它太密集了。同样,您可以在lambdaactions值中使用{{1}}来恢复您的meta-juju,但同样对于可读性而言也不会很好。

答案 2 :(得分:0)

这是一个更简单、更优雅的解决方案,它保留了嵌套列表的内部类型结构:

def isiter(x):
    return hasattr(x, '__iter__') and not isinstance(x, (str, bytes))

def apply_recursively(x, func, args=[], kwargs={}):
    if not isiter(x):
        return func(x, *args, **kwargs)
    else:
        ls_type = type(x)
        ls = [apply_recursively(item, func, args, kwargs) for item in x]
        return ls_type(ls)

例如:

>>> a = ['1', '2', '34', ('5', '67', ['8','10'])]
>>> apply_recursively(a, int)
[1, 2, 34, (5, 67, [8, 10])]

>>> b = ['forest', 'mountain', ('grass', 'cow', ['leaf','spider'])]
>>> apply_recursively(b, (lambda s, x: s+x), ['_thing'])
['forest_thing', 'mountain_thing', 
 ('grass_thing', 'cow_thing', 'leaf_thing', 'spider_thing'])]