简体   繁体   English

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

[英]Most pythonic way to recursively apply a function to string values

Question: I was wondering the most pythonic way to recursively apply a function to string values in an object without knowing it's schema in advance?问题:我想知道在事先不知道其架构的情况下,将函数递归应用于对象中的字符串值的最pythonic 方法是什么? Preferably in a generic enough manner to make it a reusable component for other types of operations.最好以足够通用的方式使其成为其他类型操作的可重用组件。

Context: I am processing a json string as input from an api request, using json.loads() to load it, before applying validations I'd like to strip off any whitespace before or after any of the strings in the object.上下文:我正在处理一个 json 字符串作为来自 api 请求的输入,使用 json.loads() 加载它,在应用验证之前我想去除对象中任何字符串之前或之后的任何空格。 I would prefer this code to be adaptive so that changes in the schema do not break it.我希望此代码具有自适应性,以便架构中的更改不会破坏它。

Current Solution:当前解决方案:

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)

Problem with Current Solution: It looks awfully terse, it's hard to understand what is going on for people who didn't write it, and I really hope there is more readable way to do this.当前解决方案的问题:它看起来非常简洁,对于没有编写它的人来说很难理解发生了什么,我真的希望有更易读的方法来做到这一点。 Or is this honestly the best it's going to get?或者这真的是最好的吗?

My suggestion is to cut down on some redundant code:我的建议是减少一些冗余代码:

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)

The same approach "reversed" would be to split cases in separate functions and map them out. “相反”的相同方法是将案例拆分为单独的函数并将它们映射出来。 It's less metamagical, but might look more readable.它不那么超魔,但可能看起来更具可读性。

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)

Note that for iterables you could "go functional" with map , but I personally find it too dense.请注意,对于可迭代对象,您可以使用map来“实现功能性”,但我个人觉得它太密集了。 Similarly, you could get your meta-juju back by using lambda in those actions values, but again it wouldn't be great for readability.同样,您可以通过在这些actions值中使用lambda来恢复您的 meta-juju,但同样,它的可读性也不是很好。

Here is a simpler and more elegant solution, that preserves the inner type structure of the nested list:这是一个更简单、更优雅的解决方案,它保留了嵌套列表的内部类型结构:

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)

For example:例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM