次のような関数があります。
function_name(step, ... , typ):
if typ == 'some type of calc method':
if step == 1:
do_me_at_step_1(...)
Elif step == 2:
do_me_at_step_2(...)
Elif ...
Elif typ == 'another calc method':
if step == 1:
do_me_at_step_1(...)
Elif ...
うまくいけば、一般的な構造はここで明確になります。異なるtyp
sの違いとそれらの処理方法は何ですか? typ
の1つのインスタンスがあり、1つの変数を他のすべての変数とは非常に異なる方法で変更します。 typ
のすべてのインスタンスは、if step == 1: ... else:
からif step == 1 ... Elif step == 5 ... else
まで、個別のステップ命令の数が異なります。最後のElif
ステートメントとelse
ステートメントは基本的に異なります。
この関数は現在恐ろしいように見えるので、どうすればこの関数をより適切に構成できますか?
私の考えは、すべての最初のステップを引き出すことでした...しかし、各typ
を「キー」になるステップに関連付ける方法が必要です。だから私はどこかに沿って考えました:
function_name(step, ... , typ):
orders = {typ1:key_step, typ2:key_step, ...}
while key_step['typ1'] != step:
if step == 1:
...
step +=1
しかし、私は2つの問題に気づきました。1つは、whileループを完全に活用していないことです。そこに「キー」ではないすべてのtyp
sの最大ステップまでifステートメントを追加する必要があります。これは、orders
。
もう1つの問題は、whileループのoutになったらです。あなたは次のようなことができると思います
if typ == 'something':
do_the_key_step_function_for_this_typ(...)
しかし、これは実際にこの機能をクリーンアップするための新しいことは何もしていません。どうすればこのコードの構造を変更して、より簡潔で短いコードにすることができますか?基本的な構造があるので、これを行うにはもっと良い方法があるはずだと思います。
この機能の詳細が必要な場合は、遠慮なく私に尋ねてください。ただし、ここに十分な詳細が記載されていることを願っています。ありがとう!
最初の関数は、1つの特定のタイプに対して1つの特定のステップを実行します。どちらも引数として提供されます。 2番目のケースでは、正しいステップを見つけようとするステップを繰り返します。
代替案1:二重辞書
代替案に基づいて、1つではなく2つのインデックスを使用して、辞書の辞書を検討することをお勧めします。例:
# just some functions to be invoked
def do_me_at_step_1():
return 1
def do_me_at_step_2():
return 2
def do_other_step_2():
return 102
# Use a dictionary assigning one type to a second level dictionary
# In the second level dictionary, assign one step with a function
command = { 'A': { 1:do_me_at_step_1, 2:do_me_at_step_2 }, 'B': { 1:do_me_at_step_1, 2:do_other_step_2 } }
# How you'd invoke the right function in function_name()
print (command['A'][1]())
print (command['A'][2]())
print (command['B'][1]())
print (command['B'][2]())
したがって、この場合、function_name()は、辞書の辞書を使用して呼び出す関数を見つけ、それらを呼び出すだけです。
代替案2:オブジェクト指向コード?
オブジェクト指向のアプローチの使用を検討することもできます。
したがって、実際には、それぞれが異なる型に対応する異なるクラスを作成します。そして、特定のクラスをインスタンス化するために、ステップに対応するメソッドを呼び出します。
実際、実装は 状態パターン に非常に近くなります。
これは クリストフの答え に基づいています。
クリストフの答えは、1組のキーがtyp
で、もう1組のキーがstep
である二重辞書の使用に焦点を当てています。ただし、これはelse
ステートメントを考慮していません。つまり、特定の時点で、step
の値に関係なく、同じ関数が使用されますが、ディクショナリは知ることができません。それ。
これを説明するための実際にはかなり単純な方法があり、それが現在使用している方法です(他の回答をより明確に表示するために非常にオープンです):デフォルトのケースで2番目の辞書を使用します。したがって、関数は次のようになります。
def function_name(step, ... , typ):
command = { 'A': { 1:do_me_at_step_1, 2:do_me_at_step_2 }, 'B': { 1:do_me_at_step_1, 2:do_other_step_2 } }
default_command = {'A':final_function_for_A, 'B':final_function_for_B, ...}
try:
return command[typ][step](inputs to function)
except KeyError:
return default_command[typ](inputs to function)
これは私が持っていたものよりもずっといいです。
編集:エラーの検出はコストがかかるため、最後の4行はおそらくif-elseの方が優れています。
if step in command[typ].keyValues():
return command[typ][step](...)
else:
return default_command[typ](...)
関数をパラメーターとして渡し、キーステップ中に呼び出すと、一般的なステップの間にtyp関数を呼び出すことができます。
# defining some dummy functions as the common code
def f1(*args):
pass
def f2(*args):
pass
def f3(*args):
pass
# defining some functions for a specific 'typ'
def integer(*args):
pass
def complex(*args):
pass
# a dictionary that 1-indexes the common code
common_code = {
1 : f1,
2 : f2,
3 : f3,
# some lambdas in case your common code is simple or short
4 : (lambda *args: args),
5 : (lambda *args: args),
}
# the main function that lets us call a function for a chosen keystep
def calc_switch(step, calc_typ, *args):
last = len(common_code)
# perform the common steps from 1 until the chosen step
# this for loop will not execute code if the user chooses 1
for i in range(1, step):
common_code[i](args)
# the call to our step-dependent function
calc_typ(args)
# Continue calculating common code
# if you want to exclude the step chosen by the user, then
# set the first argument of range to step+1
for i in range(step, last + 1):
common_code[i](args)
# some example calls, where the first argument is the key-step
calc_switch(2, complex)
calc_switch(4, integer)
これで、キーステップ関数を1回定義し、共通の動作を1回定義するだけで済みます。共通コードに対する追加の変更は、共通コードディクショナリで行われます。
さらに、「typ」の呼び出しに分離したい場合は、部分的な関数を使用できます。例えば:
from functools import partial
do_integer = partial(calc_switch, 2, complex)
do_complex = partial(calc_switch, 4, integer)
これで完全なdo_integer
すべての一般的なタスクを実行し、定義されたキーステップでtyp 'integer'の計算を実行し、そのキーステップの後に一般的な関数を実行し、引数を取り、ベース関数に(* argsを介して)渡しますパラメータ)。
これが役に立てば幸いです!コードはPython 2.7にありますが、辞書のものと部分的なアプリケーションのものはすべてPython 3.にあります。