【问题标题】:How to reduce the complexity of several for loops in python如何降低python中几个for循环的复杂度
【发布时间】:2021-06-25 07:24:15
【问题描述】:

我有以下功能

import requests

children_dict = {}
def get_list_of_children(base_url, username, password, folder="1"):
    token = get_token()
    url = f"{base_url}/unix/repo/folders/{folder}/list"
    json = requests_json(url,token)
    for obj in json["list"]:
        if obj['name'] == 'MainFolder':
            folderId = obj['id']
            url_parent = f"{base_url}/unix/repo/folders/{folderId}/list"
            json_parent = requests_json(url_parent,token)
            for obj_child in json_parent['list']:
                if obj_child['folder'] == True:
                    folder_grand_child_id = obj_child['id']
                    url_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_child_id}/list"
                    json_grand_child = requests_json(url_grand_child,token)
                    for obj_grand_child in json_grand_child["list"]:
                        if obj_grand_child['name'] == 'SubFolder':
                            folder_grand_grand_child = obj_grand_child['id']
                            url_grand_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_grand_child}/list"
                            json_grand_grand_child = requests_json(url_grand_grand_child,token)
                            for obj_grand_grand_child in json_grand_grand_child["list"]:
                                if obj_grand_grand_child['name'] == 'MainTasks':
                                    folder_grand_grand_grand_child = obj_grand_grand_child['id']
                                    url_grand_grand_grand_child = f"{base_url}/unix/repo/folders/{folder_grand_grand_grand_child}/list"
                                    json_grand_grand_grand_child = requests_json(url_grand_grand_grand_child,token)
                                    for obj_grand_grand_grand_child in json_grand_grand_grand_child["list"]:
                                        children_dict[[obj_grand_grand_grand_child['id']] = obj_grand_grand_grand_child['name']
                                    
                            

        
        
    
    return children_dict

我在这里要完成的是重复调用 api 以遍历 http 文件夹结构以获取最后一个目录中的文件列表 该功能按预期工作,但 sonarlint 出现以下错误

Refactor this function to reduce its Cognitive Complexity from 45 to the 15 allowed. [+9 locations]sonarlint(python:S3776)

有没有更好的方法来处理这个函数?

任何人都可以重构这个,直接指出正确的方法就可以了

【问题讨论】:

  • 也许你需要 xpath
  • 它的 api 调用,我没有使用 web 元素
  • 也许你可以使用 List Comprehensions
  • 或者至少您可以将它们拆分为短函数,并具有有意义的名称。
  • 也许this post对你有用

标签: python


【解决方案1】:

这不是一个完整的解决方案,但要更一般地回答您的问题“我怎样才能简化这个”,您需要在代码中寻找重复的模式并将它们概括为一个函数。也许这是一个您可以递归或循环调用的函数。例如,在你的那个深深嵌套的陈述中,它只是一遍又一遍的相同模式,比如:

    url = f"{base_url}/unix/repo/folders/{folder}/list"
    json = requests_json(url,token)
    for obj in json["list"]:
        if obj['name'] == '<some folder name>':
            folderId = obj['id']
            # ...repeat...

所以试着把它概括成一个循环,也许,像:

url_format = "{base_url}/unix/repo/folders/{folder_id}/list"
folder_hierarchy = ['MainFolder', 'SubFolder', 'MainTasks']
folder_id = '1'  # this was the argument passed to your function
for subfolder in folder_hierarchy:
    url = url_format.format(base_url=base_url, folder_id=folder_id)
    folder_json = requests_json(url, token)
    for obj in folder_json['list']:
        if obj['name'] == subfolder:
            folder_id = obj['id']
            break
    # Now the pattern repeats for the next level of the hierarchy but
    # starting with the new folder_id

这只是示意图,您可能需要进一步概括,但这是一个想法。

如果您的目标是遍历更复杂的层次结构,您可能需要研究树遍历算法。

【讨论】:

  • 在这种情况下,看起来 DFS 可能会有所帮助
  • 确实,我的回答过于简单,只是为了大致了解如何概括这样的事情。实际上,OP 可能希望采用相同的想法并将其实现为深度优先遍历。这可以通过像堆栈一样使用folder_hierarchy 的轻微修改来完成。
【解决方案2】:

有很多重复的代码。一旦确定了重复模式,就可以将它们提取到类和函数中。特别是,我发现将所有 Web API 逻辑与其余代码隔离开来很有用:

class Client:
    def __init__(self, base_url, token):
        self.base_url = base_url
        self.token = token

    def list_folder(self, folder_id):
      return request_json(
          f'{self.base_url}/unix/repo/folders/{folder_id}/list', self.token
      )['list']

    def get_subfolders(self, parent_id=1):
        return [c for c in self.list_folder(parent_id) if c['folder']]

    def get_subfolder(self, parent_id, name):
        children = self.list_folder(parent_id)
        for child in children:
            if child['name'] == name:
                return child
        return None

    def resolve_path(self, path, root_id=1):
        parent_id = root_id
        for p in path:
            current = self.get_subfolder(parent_id, p)
            if not current:
                return None
            parent_id = current['id']

        return current

现在您可以使用上面的类来简化主要代码:

client = Client(base_url, token)
for folder in client.get_subfolders():
    child = client.resolve_path(folder['id'], ('SubFolder', 'MainTasks'))
    if child:
       # do the rest of the stuff

上面的代码不保证能按原样工作,只是一个想法的说明。

【讨论】:

    【解决方案3】:

    我无法真正对其进行测试,但我会将其设置为如下所示,以便轻松构建多层重复代码。

    class NestedProcessing:
        def __init__(self, base_url):
            self.base_url = base_url
            self.token = get_token()
            self.obj_predicates = []
            
        def next_level_predicate(self, obj_predicate):
            self.obj_predicates.append(obj_predicate)
            return self
            
        def final_action(self, obj_action):
            self.obj_action = obj_action
            return self
            
        def process(self, first_folder_id):
            self.process_level(0, first_folder_id)
                        
        def process_level(self, index, folder_id):
            obj_is_good = self.obj_predicates[index]
            url = f"{self.base_url}/unix/repo/folders/{folder_id}/list"
            json = requests_json(url, self.token)
            for obj in json["list"]:
                if index == len(self.obj_predicates) - 1: # last level
                    self.obj_action(obj)
                elif obj_is_good(obj):
                    self.process_level(index + 1, obj['id'])
    
            
    
    def get_list_of_children(base_url, username, password, folder="1"):
        children_dict = {}
        NestedProcessing(base_url)
            .next_level_predicate(lambda obj: obj['name'] == 'MainFolder')
            .next_level_predicate(lambda obj: obj['folder'] == True)
            .next_level_predicate(lambda obj: obj['name'] == 'SubFolder')
            .next_level_predicate(lambda obj: obj['name'] == 'MainTasks')
            .final_action(lambda obj, storage=children_dict: storage.update({obj['id']: obj['name']})
            .process(folder)
        return children_dict
    

    【讨论】:

    • 你能检查这一行吗def process(self, first_folder_id): self.process_level(index=0, first_folder_id)我收到错误Positional argument cannot appear after keyword arguments
    • 是的,只需删除index= 部分,让它成为self.process_level(0, first_folder_id)
    • NameError: name 'process_level' is not defined你能看看这个吗?
    • 替换为self.process_level
    • 我在 lambda 语句中收到错误 KeyError: '221037'
    猜你喜欢
    • 1970-01-01
    • 2021-11-26
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多