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



问题:我想知道在事先不知道它的模式的情况下递归地将函数应用于对象中的字符串值的最python方法? 最好以足够通用的方式使其成为其他类型的操作的可重用组件。

上下文:我正在处理一个 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)

当前解决方案的问题:它看起来非常简洁,很难理解没有写它的人发生了什么,我真的希望有更可读的方法可以做到这一点。 或者老实说,这是

最好的吗?

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

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)

"颠倒"的相同方法是将案例拆分为单独的函数并映射出来。它不那么元,但可能看起来更具可读性。

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)

请注意,对于可迭代对象,您可以使用map"功能化",但我个人认为它太密集了。同样,您可以通过在这些actions值中使用lambda来恢复元 juju,但同样,这对可读性也不是很好。

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

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'])]

最新更新