代码之家  ›  专栏  ›  技术社区  ›  sqram

需要帮助理解这个递归函数是如何工作的

  •  2
  • sqram  · 技术社区  · 15 年前

    这是一个函数(归功于用户abbot,在另一个问题中提供了它)

    def traverse(ftp):
    
        level = {}
        for entry in (path for path in ftp.nlst() if path not in ('.', '..')):
            ftp.cwd(entry)
            level[entry] = traverse(ftp) 
            ftp.cwd('..')
        return level
    

    我不明白的是:当python进入函数时,它会创建一个空字典。( level )在for循环中,它将目录名存储为字典中的键。至于这个键的值,python再次进入函数并搜索一个目录,它就变成了这个键的值。

    但是,级别字典是如何记住里面的值的呢?我的意思是,每次python进入函数时,不应该重置/清空它吗?

    5 回复  |  直到 15 年前
        1
  •  7
  •   badp    15 年前

    不。函数的每个“实例”都有自己的 level 而且在不同的复制品之间没有副作用 水平 .

    使用此文件夹树:

    root
     `-home
        |- lyrae
        |   |- ftp.py
        |   `- http.py
        `- badp
    

    以下是调用时的(简化的)执行流 ftp root :

    • ftp(root) 创建一个空 水平 词典
    • FTP(根) 枚举子文件夹: (home) .
    • FTP(根) 选择第一个子文件夹并将目录更改为它。
    • FTP(根) 集合 level[home] 结果是 FTP协议 在当前文件夹中。

    • ftp(home) 创建一个空 水平 词典
    • FTP(家庭) 枚举子文件夹: (lyrae, badp) .
    • FTP(家庭) 选择第一个子文件夹并将目录更改为它。
    • FTP(家庭) 集合 level[lyrae] 结果是 FTP协议 在当前文件夹中。

    • ftp(lyrae) 创建一个空 水平 词典
    • FTP(LyRAE) 枚举子文件夹: () .
    • ftp(天琴座) 子文件夹不足,无法分析并返回 水平 .

    • FTP(家庭) 完成任务: levels = {'lyrae': {}}
    • FTP(家庭) 更改到下一个文件夹。
    • FTP(家庭) 集合 level[badp] 结果是 FTP协议 在当前文件夹中。

    • ftp(badp) 创建一个空 水平 词典
    • 文件传输协议(BADP) 枚举子文件夹: () .
    • 文件传输协议(BADP) 子文件夹不足,无法分析并返回 水平 .

    • FTP(主页) 完成任务: levels = {'lyrae': {}, 'badp': {}}
    • FTP(主页) 子文件夹不足,无法分析并返回 水平 .

    • FTP(根) 完成任务: levels = {'home': {'lyrae': {}, 'badp': {}}}
    • FTP(根) 子文件夹不足,无法分析并返回 水平 .
        2
  •  2
  •   Benj    15 年前

    我想,这些其他的答案还不够解释。此函数的每个递归入口都会创建一个新的本地级字典。但最关键的是,它也会回归。这意味着每个递归中级别的本地版本将成为级别的字典树。一旦递归展开,就剩下了一个相互引用的字典树。这意味着创建的局部变量不会被垃圾收集,因为在堆栈上有一个对顶级字典的引用,该字典是从最外层的函数返回的。

        3
  •  1
  •   Amnon    15 年前

    level 是局部变量。函数的每个“运行”都有自己的变量 水平 ,这样变量就不会互相干扰。

        4
  •  1
  •   Caleb Hattingh    15 年前

    范围 level 仅限于功能。即使函数调用自己,也不意味着 那个 函数调用的内部变量(A 不同的 水平 )与 一个人的

        5
  •  1
  •   SilentGhost    15 年前

    变量 level 只存在于函数的作用域中,在函数的末尾丢弃局部变量,因此对于 traverse 会有它自己的 水平 字典。不会重写或重写任何内容。