私はこのコードを持っています:
_fields = ['name','email']
def clean_name():
pass
def clean_email():
pass
_
clean_name()
およびclean_email()
を動的に呼び出すにはどうすればよいですか?
例えば:
_for field in fields:
clean_{field}()
_
PHPでそれを行う方法だったが、明らかに機能しないため、中括弧を使用しました。
Pythonでこれを行う方法は?
globals, vars
を使用したくない場合、および動的に呼び出す関数をカプセル化するために別のモジュールやクラスを作成したくない場合は、それらを現在のモジュールの属性として呼び出すことができます。
import sys
...
getattr(sys.modules[__name__], "clean_%s" % fieldname)()
globals()
を調べるよりも、そのような関数の辞書を用意する方が良いでしょう。
通常のアプローチは、そのような関数を持つクラスを書くことです。
_class Cleaner(object):
def clean_name(self):
pass
_
そして、getattr
を使用してそれらにアクセスします。
_cleaner = Cleaner()
for f in fields:
getattr(cleaner, 'clean_%s' % f)()
_
さらに移動して、次のようなこともできます。
_class Cleaner(object):
def __init__(self, fields):
self.fields = fields
def clean(self):
for f in self.fields:
getattr(self, 'clean_%s' % f)()
_
次に、それを継承し、継承クラスで_clean_<name>
_メソッドを宣言します。
_cleaner = Cleaner(['one', 'two'])
cleaner.clean()
_
実際には、これをさらに拡張して、よりクリーンにすることができます。そのようなメソッドがクラスに存在する場合、最初のステップはおそらくhasattr()
でチェックを追加することです。
global
を使用することは、これを行う非常に悪い方法です。次のようにしてください。
_fields = {'name':clean_name,'email':clean_email}
for key in fields:
fields[key]()
_
関数を辞書の値にマップします。
vars()[]
の使用も間違っています。
globals()
は、グローバル名前空間のdict
を提供します。これから、必要な機能を取得できます。
f = globals()["clean_%s" % field]
次に呼び出します:
f()
別の方法を次に示します。
myscript.py:
def f1():
print 'f1'
def f2():
print 'f2'
def f3():
print 'f3'
test.py:
import myscript
for i in range(1, 4):
getattr(myscript, 'f%d' % i)()
私はこの問題に2度遭遇し、最終的にsafeとnotいではないの解決策を思いつきました(私の謙虚な意見です)。
[〜#〜] recap [〜#〜]以前の回答:
globalsはハッキング、高速かつ簡単な方法ですが、関数名と非常に一貫性がなければならず、変数が上書きされると実行時に壊れる可能性があります。また、それは非Pythonicで、安全でなく、非倫理的です。
辞書(つまり、文字列から関数へのマップ)はより安全で使いやすいです...私のファイル全体で辞書の割り当て、それは簡単に失われます。
Decorators辞書ソリューションを私のためにまとめました。デコレータは、副作用と変換を関数定義に添付するための非常に良い方法です。
例の時間
fields = ['name', 'email', 'address']
# set up our function dictionary
cleaners = {}
# this function will add stuff into the dictionary
def add_cleaner(key):
# this is a parametered decorator, it returns the actual decorator
def actual_decorator(func):
# add func to the dictionary
cleaners[key] = func
return func
return actual_decorator
クリーナー関数を定義するたびに、これを宣言に追加します。
@add_cleaner('email')
def email_cleaner(email):
#do stuff here
return result
関数は、解析されるとすぐに辞書に追加され、次のように呼び出すことができます。
cleaned_email = cleaners['email'](some_email)
スクリプトの最後にこの行を追加して、忘れないようにしてください。 ;)
assert(set(cleaners.keys()).issubset(fields))
for field in fields:
vars()['clean_' + field]()
別の方法は次のとおりです。関数を定義してから、名前をキーとして辞書を定義します。
>>> z=[clean_email, clean_name]
>>> z={"email": clean_email, "name":clean_name}
>>> z['email']()
>>> z['name']()
次に、名前をキーとしてループします。
またはこれはどうですか?文字列を作成し、「eval」を使用します。
>>> field = "email"
>>> f="clean_"+field+"()"
>>> eval(f)
その後、ループしてevalの文字列を作成します。
評価のために文字列を作成する必要があるメソッドは、kludgyと見なされることに注意してください。
フィールド名をクリーニング関数にマッピングする辞書を使用します。いくつかのフィールドに対応するクリーニング関数がない場合、それらを処理するfor
ループは、それらの場合に何らかのデフォルト関数を提供することで単純に保つことができます。ここに私が意味するものがあります:
fields = ['name', 'email', 'subject']
def clean_name():
pass
def clean_email():
pass
# (one-time) field to cleaning-function map construction
def get_clean_func(field):
try:
return eval('clean_'+field)
except NameError:
return lambda: None # do nothing
clean = dict((field, get_clean_func(field)) for field in fields)
# sample usage
for field in fields:
clean[field]()
上記のコードは、対応する関数clean_<field>
は、fields
リストで指定された名前ごとに存在します。フィールドリストまたは使用可能なクリーニング機能が変更されない限り、同じままであるため、おそらく1回実行するだけで済みます。