意外的NameError

时间:2011-07-18 12:00:39

标签: python

我正在编写一个脚本来批量重命名文件夹中的所有文件。我试图使其模块化,因此核心算法(生成新文件名的算法)很容易交换。

这是我到目前为止所得到的:

from os import listdir, rename

def renamer(path, algorithm, counter=False, data=None, data2=None, safe=True):

    call_string = 'new=algorithm(i'
    if counter:
        call_string += ', file_index'
    if data != None:
        call_string += ', data'
    if data2 != None:
        call_string += ', data2'
    call_string += ')'

    if safe:
        print('Press Enter to accept changes. '+
        'Type anything to jump to the next file.\n')

    files_list = listdir(path)
    for i in files_list:
        file_index = files_list.index(i)
        old = i
        exec(call_string)
        if safe:
            check = input('\nOld:\n'+old+'\nNew:\n'+new+'\n\nCheck?\n\n')
            if check is not '':
                continue
        rename(path+old, path+new)

    return

现在由于某种原因(对我来说似乎无法解释),调用该函数会引发NameError:

>>> def f(s):
    return 'S08'+s

>>> path='C:\\Users\\****\\test\\'
>>> renamer(path, f)
Press Enter to accept changes. Type anything to jump to the next file.

Traceback (most recent call last):
  File "<pyshell#39>", line 1, in <module>
    renamer(path, f)
  File "C:\Python32\renamer.py", line 25, in renamer
    check = input('\nOld:\n'+old+'\nNew:\n'+new+'\n\nCheck?\n\n')
NameError: global name 'new' is not defined

无法解释,因为在第25行它应该已经执行了call_string,从而定义了名称new。 我一直试图弄清楚我的错误超过一个小时了,我已经完成整个代码进入线路两次进入shell,它运行良好,我似乎无法弄清楚问题。

有人可以帮我弄清楚我哪里出错吗?

编辑: 我已经猜到你可能无法使用exec分配名称,所以我测试了它如下,并且它有效:

>>> exec('cat="test"')
>>> cat
'test'

4 个答案:

答案 0 :(得分:3)

不要使用exec或eval,只需编写

即可
new = algorithm(i, file_index, data, data2)

确保所有算法都可以使用4个参数(忽略它们不需要的参数)。

如果您不喜欢这样,以下内容比使用eval更具有pythonic和效率:

args = [i] 
if counter:
    args.append(file_index)
for arg in (data, data2):
    if arg is not None:
        args.append(arg)

new = algorithm(*args)

还要取代丑陋的:

for i in files_list:
    file_index = files_list.index(i)

for index, filename in enumerate(file_list):
    ...

最后,使用os.path.join来连接路径部分而不是字符串连接。当您使用目录名称调用函数而没有尾随'/'

时,这将节省您的调试时间

答案 1 :(得分:1)

您在new - 来电中声明了名称exec。它在外面是不可见的。这就是为什么当您在调用new之后尝试访问exec而不是exec内部时,会生成错误。

我认为你没有任何理由在这里首先使用exec。您构建call_string的方式,可以直接调用algorithm

如果您真的希望算法能够采用变量参数,请使用keyword arguments

答案 2 :(得分:1)

您不需要exec。您可以调整传递给函数的参数,使其适应:

def renamer(path, algorithm, counter=False, data=None, data2=None, safe=True):

    if safe:
        print('Press Enter to accept changes. '+
        'Type anything to jump to the next file.\n')

    files_list = listdir(path)
    for file_index, old in enumerate(files_list):
        opt_args = []
        if counter:
            opt_args.append(file_index)
        if data is not None:
            opt_args.append(data)
        if data2 is not None:
            opt_args.append(data2)
        new = algorithm(old, *opt_args)
        if safe:
            check = input('\nOld:\n'+old+'\nNew:\n'+new+'\n\nCheck?\n\n')
            if check:
                continue
        rename(path+old, path+new)

其他一些小问题:使用“不是无”代替“!=无”;要查看字符串是否为空,只需使用“if check”;而且你不需要在函数结束时返回。我还包括@gurney alex建议的enumerate改进。

答案 3 :(得分:0)

call_string = 'new=algorithm(i'更改为call_string = 'algorithm(i'并将exec(call_string)更改为new = eval(call_string),您不应该遇到任何问题。