2011-07-18 15 views
3

フォルダ内のすべてのファイルの名前を一括して変更するスクリプトを作成しています。私はモジュラー化しようとしているので、コアのアルゴリズム(新しいファイル名を生成するアルゴリズム)は簡単に交換できます。ここで予期しない名前エラー

は、私がこれまで持っているものです:

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 

何らかの理由で(私にunexplicableそうです)、関数を呼び出すことは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 

Unexplicableライン25で、ので、すでにcall_stringを実行していて、新しい名前を定義しているはずです。 私は1時間以上私の間違いを解明しようとしていました。私はコード全体を2行目の行に入力していましたが、うまくいきました。問題を理解できないようです。

誰かが私がどこに間違っているのか理解してもらえますか?

編集: は、私はすでにあなたが幹部を使用して名前を割り当てることができない可能性があります推測してきましたので、以下のように私はそれをテストしてみた、それが働いた:

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

ええと、ugh、ugh !!!!このように 'exec'を使わないでください!人々が自分の名前をつくって_関数を書くことを許可して、それらの周りを渡しましょう! – katrielalex

答えて

3

は、このためのexecやevalを使用して、ちょうど

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

を書いていないすべてのアルゴリズムが4つの引数(彼らが必要としないこれらを無視して)を使用することができることを確認してください。

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

new = algorithm(*args) 

はまた醜い置き換える:

あなたがこれを気に入らない場合は、次はもっとニシキヘビかつ効率的にevalを使用するよりもある

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

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

最後に、os.path.joinを使用して、文字列連結の代わりにパス部分を連結します。これにより、後に '/'を付けないでディレクトリ名を指定して関数を呼び出すとデバッグ時間が節約されます

+0

thx、すてきなヒント。私は文字列を使ってアルゴリズムを呼び出すことにしていますが、引数を使って関数を定義することは邪魔になることはありません^^ – Axim

+1

@Axim:私はあなたが本当にevalとexecの使用を制限したいと主張しています。あなたのPythonコード。私はあなたのアルゴリズムに不要な引数を追加しないようにする回避方法を追加しました。 –

+2

@Axim: 'exec'と' eval'は正当な使用法を持っていますが、これは正当なものではありません!経験則は、exec/evalを 'code smell' _a priori_と見なし、自分自身を証明するために真剣に努力した後でのみ、これらを許可することです。 – mjv

1

あなたが名前newを宣言exec-call内にあります。それは外には見えません。そのため、execへの呼び出し後にnewにアクセスしようとすると、エラーが発生し、execにはアクセスできません。

ここに最初にexecを使用する理由はありません。あなたがcall_stringを構築する方法は、algorithmと直接呼び出すことができます。

アルゴリズムで可変引数を使用できるようにしたい場合は、keyword argumentsを使用してください。

+0

私がcall_stringを構築する方法は、時には4つまでの引数を使用できるようにしたい場合がありますが、(例のように)時には1つだけを使用したい場合もあるからです。 – Axim

+0

'algorithm'は1から4までの引数をとることができます。これはあなたのドキュメントに書いておいてください。 –

0

call_string = 'new=algorithm(i'からcall_string = 'algorithm(i'に変更し、exec(call_string)new = eval(call_string)に変更しても問題ありません。

+0

thxはうまくいきました:) – Axim

+0

@Aximは、あなたが望むものなら答えを受け入れます。 – agf

+1

@Axim:これはうまくいくかもしれませんが、 'exec'と' eval'はほとんど決して適切な選択ではありません(http://stackoverflow.com/questions/1832940/)。 Pythonプログラマーとしてのスキルを向上させたい場合は、ここで使用しないでください。お願いします。 –

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) 

他のいくつかのマイナーなポイント:代わりに「=なし!」「Noneでない」を使用します。文字列が空であるかどうかを確認するには、 "if check"を使用します。あなたは関数の最後に裸のリターンを必要としません。私はまた、@ gurney alexによって提案されたenumerate改善を含んでいます。

関連する問題