web-dev-qa-db-ja.com

基礎となる構造を使用するように多数のif-Elif-elseステートメントを変更する

次のような関数があります。

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 ...

うまくいけば、一般的な構造はここで明確になります。異なるtypsの違いとそれらの処理方法は何ですか? 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ループを完全に活用していないことです。そこに「キー」ではないすべてのtypsの最大ステップまでifステートメントを追加する必要があります。これは、orders

もう1つの問題は、whileループのoutになったらです。あなたは次のようなことができると思います

if typ == 'something':
    do_the_key_step_function_for_this_typ(...)

しかし、これは実際にこの機能をクリーンアップするための新しいことは何もしていません。どうすればこのコードの構造を変更して、より簡潔で短いコードにすることができますか?基本的な構造があるので、これを行うにはもっと良い方法があるはずだと思います。

この機能の詳細が必要な場合は、遠慮なく私に尋ねてください。ただし、ここに十分な詳細が記載されていることを願っています。ありがとう!

3
heather

最初の関数は、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:オブジェクト指向コード?

オブジェクト指向のアプローチの使用を検討することもできます。

したがって、実際には、それぞれが異なる型に対応する異なるクラスを作成します。そして、特定のクラスをインスタンス化するために、ステップに対応するメソッドを呼び出します。

実際、実装は 状態パターン に非常に近くなります。

5
Christophe

これは クリストフの答え に基づいています。


クリストフの答えは、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](...)
4
heather

関数をパラメーターとして渡し、キーステップ中に呼び出すと、一般的なステップの間に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.にあります。

1
Isaac Moreno