首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么这个递归函数似乎与我预期的相反呢?

为什么这个递归函数似乎与我预期的相反呢?
EN

Stack Overflow用户
提问于 2017-01-13 00:17:24
回答 3查看 63关注 0票数 1

这是我的Python2.7脚本。

代码语言:javascript
复制
import os.path, shutil

def makeFolderName(num, dirBase='./interactive'):
    destDir=    dirBase + '-' + str(num) + '/'
    if os.path.isdir(destDir):
        num += 1
        makeFolderName(num)
    else:
        shutil.copytree(dirBase, destDir)

    print destDir
    return destDir


folderName= makeFolderName(1)

print folderName

这是打印输出。

代码语言:javascript
复制
./interactive-18/
./interactive-17/
./interactive-16/
./interactive-15/
./interactive-14/
./interactive-13/
./interactive-12/
./interactive-11/
./interactive-10/
./interactive-9/
./interactive-8/
./interactive-7/
./interactive-6/
./interactive-5/
./interactive-4/
./interactive-3/
./interactive-2/
./interactive-1/
====./interactive-1/

在该方法中,我认为只有当destDir的值不是目录时才会打印destDir。相反,它会将num从其最高值打印到最低值。

folderName的价值是最低的num,而不是最高的。

我希望makeFolderName返回一个目录名,其中num比当前目录中的要高。

我做错了什么?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-01-13 00:22:53

您的错误是应该在递归调用之后返回:

代码语言:javascript
复制
    if os.path.isdir(destDir):
        num += 1
        return makeFolderName(num)
    else:
        shutil.copytree(dirBase, destDir)

如果不这样做,那么当makeFolderName返回该函数时,将继续运行以下代码,即num的打印。但是,由于它是在递归调用返回后完成的,所以您可以看到相反的数字。

票数 3
EN

Stack Overflow用户

发布于 2017-01-14 01:21:11

因为您需要在递归点返回,这将停止执行当前调用的其余部分:

代码语言:javascript
复制
if os.path.isdir(destDir):
    num += 1
    return makeFolderName(num)

还有一些事情值得一提:

(1)重新分配参数通常是值得避免的,在这里没有必要。而不是:

代码语言:javascript
复制
if os.path.isdir(destDir):
    num += 1
    return makeFolderName(num)

你可以写得更简单:

代码语言:javascript
复制
if os.path.isdir(destDir):
    return makeFolderName(num + 1)

(2)如果到达默认Python最大递归深度,递归将失败,这可能会在几分钟、几个小时、几天或几周内发生,这取决于您的应用程序。如果您有1000+ ./interactive-n目录,您将得到一个异常。

这里有一个迭代版本,如果有很多目录,它不会被删除。它使用了一些其他Python特性来简化和澄清代码。

注意使用下划线而不是camelCase的类Python变量和函数名越多。

(3)我将代码分开:

  • 若要查找新目录名,请执行以下操作
  • 创建新目录

它使你所做的变得更加明显。如果您从一开始就将关注点分开,那么当您的函数库发展成为一个大得多的应用程序,并且您正在试图跟踪令人费解的行为时,您会感谢自己。

代码语言:javascript
复制
import os
import shutil


def make_new_dir(num, original_dir='./interactive'):
    new_dir = new_dirname(num, original_dir)
    shutil.copytree(original_dir, new_dir)
    return new_dir


def new_dirname(startnum, stem):
    num = startnum
    while True:
        # Python 2.7-3.5
        name = '{0}-{1}/'.format(stem, num)
        # Python 3.6
        # name = f'{stem}-{num}/'
        if not os.path.isdir(name):
            break
        num += 1
    return name


dirname = make_new_dir(1)
print(dirname)

(4)绩效。每次调用原始方法时,它都会从num重新扫描现有的./interactive-n目录。下面是一个基于生成器的版本,它可以记住它的位置,并从那里进行搜索:

代码语言:javascript
复制
import os
import shutil


def make_new_dir(num, original_dir='./interactive'):
    find_new_dir = unused_dirname(num, original_dir)
    while True:
        new_dir = next(find_new_dir)
        shutil.copytree(original_dir, new_dir)
        yield new_dir


def unused_dirname(startnum, stem):
    num = startnum
    while True:
        # Python 2.7-3.5
        name = '{0}-{1}/'.format(stem, num)
        # Python 3.6
        # name = f'{stem}-{num}/'
        if not os.path.isdir(name):
            yield name
        num += 1


new_dir = make_new_dir(1)
print(next(new_dir))
print(next(new_dir))
print(next(new_dir))


>>> python makedir.py
./interactive-20/
./interactive-21/
./interactive-22/

(5)出于性能原因,您最终可能更喜欢使用glob检查现有目录(和文件)

代码语言:javascript
复制
>>> import glob
>>> glob.glob(r'./interactive-*')
['./interactive-1', './interactive-10', './interactive-11', './interactive-12', './interactive-13', './interactive-14', './interactive-15', './interactive-16', './interactive-17', './interactive-18', './interactive-19', './interactive-2', './interactive-20', './interactive-21', './interactive-22', './interactive-23', './interactive-24', './interactive-25', './interactive-3', './interactive-4', './interactive-5', './interactive-6', './interactive-7', './interactive-8', './interactive-9']

使用Python的一些优点,您可以为每个文件提取编号后缀:

代码语言:javascript
复制
>>> interactives = glob.glob(r'.\interactive-*')
>>> existing_dir_numbers = [int(interactive.split('-')[-1]) for interactive in interactives]

.split('-') splits a string into list elements at each dash. a_list[-1] returns the last element in a list. Since we know we're adding a-nsuffix to our directories, the last element is always a string version ofn, which we convert to a number usingint()`.

代码语言:javascript
复制
>>> existing_dir_numbers
[1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25, 3, 4, 5, 6, 7, 8, 9]
>>> max(existing_dir_numbers)
25

所以现在您可以选择./interactive-26作为新的目录名。

票数 2
EN

Stack Overflow用户

发布于 2017-01-13 00:30:06

我认为这是您想要的更简单和迭代的代码:

代码语言:javascript
复制
def makeFolderName(num, dirBase='./interactive'):
    destDir = dirBase + '-' + str(num) + '/'
    while os.path.isdir(destDir):
        num += 1
        destDir = dirBase + '-' + str(num) + '/'

    shutil.copytree(dirBase, destDir)

    print destDir
    return destDir

在这里,我们循环直到找到一个不存在的interactive-num,然后将文件复制到该目录中,并返回编号最高的目录名。

顺便说一句,根据这个答案的说法,如果使用递归解决方案,默认情况下,一旦您到达interactive-1000,Python就会崩溃,因为Python对允许的递归深度有一个定义的限制。

或者,如果您不喜欢重复destDir分配:

代码语言:javascript
复制
def makeFolderName(num, dirBase='./interactive'):
    destDir = ''
    while True:
        if not os.path.isdir(destDir):
            break
        num += 1
        destDir = dirBase + '-' + str(num) + '/'

    shutil.copytree(dirBase, destDir)

    print destDir
    return destDir

顺便说一句,如果您有一个名为interactive-num的文件,而不是目录,它将被覆盖,除非您添加了如下内容:

代码语言:javascript
复制
if not (os.path.isdir(destDir) or os.path.isfile(destDir[:-1]):

检查文件时不要忘记删除'/'

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41625404

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档