表現 - python 辞書 switch




Pythonでのswitch文の置き換え? (20)

Google検索でどこでも探していた簡単な答えは見つかりませんでした。 しかし、とにかくそれを理解しました。 それは本当に簡単です。 それを掲示することを決め、おそらく他の誰かの頭のほんの少しの傷を防ぐ。 キーは単純に「イン」とタプルです。 RANDOMフォールスルーを含む、フォールスルーによるswitch文の動作を次に示します。

l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
     'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']

for x in l:
    if x in ('Dog', 'Cat'):
        x += " has four legs"
    elif x in ('Bat', 'Bird', 'Dragonfly'):
        x += " has wings."
    elif x in ('Snake',):
        x += " has a forked tongue."
    else:
        x += " is a big mystery by default."
    print(x)

print()

for x in range(10):
    if x in (0, 1):
        x = "Values 0 and 1 caught here."
    elif x in (2,):
        x = "Value 2 caught here."
    elif x in (3, 7, 8):
        x = "Values 3, 7, 8 caught here."
    elif x in (4, 6):
        x = "Values 4 and 6 caught here"
    else:
        x = "Values 5 and 9 caught in default."
    print(x)

提供:

Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.

Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default.

Pythonで入力インデックスの値に基づいて異なる固定値を返す関数を記述したいと思います。

他の言語ではswitch文またはcase文を使用しますが、Pythonにはswitch文がないようです。 このシナリオで推奨されるPythonソリューションは何ですか?


Twisted Pythonコードから学んだパターンがあります。

class SMTP:
    def lookupMethod(self, command):
        return getattr(self, 'do_' + command.upper(), None)
    def do_HELO(self, rest):
        return 'Howdy ' + rest
    def do_QUIT(self, rest):
        return 'Bye'

SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'
SMTP().lookupMethod('QUIT')('') # => 'Bye'

トークンにディスパッチして拡張コードを実行する必要があるときはいつでも使用できます。 ステートマシンでは、 state_メソッドを持ち、 state_ディスパッチしself.state 。 このスイッチは、基本クラスを継承し、独自のdo_メソッドを定義することで、きれいに拡張できます。 しばしば、基本クラスにdo_メソッドもないでしょう。

編集:どのように正確に使用されている

SMTPの場合は、電線からHELOを受け取ります。 関連するコード( twisted/mail/smtp.pyから、私たちの場合に変更された)は次のようになります

class SMTP:
    # ...

    def do_UNKNOWN(self, rest):
        raise NotImplementedError, 'received unknown command'

    def state_COMMAND(self, line):
        line = line.strip()
        parts = line.split(None, 1)
        if parts:
            method = self.lookupMethod(parts[0]) or self.do_UNKNOWN
            if len(parts) == 2:
                return method(parts[1])
            else:
                return method('')
        else:
            raise SyntaxError, 'bad syntax'

SMTP().state_COMMAND('   HELO   foo.bar.com  ') # => Howdy foo.bar.com

あなたは' HELO foo.bar.com '受け取るでしょう(または、あなたは'QUIT'または'RCPT TO: foo'得るかもしれません)。 これは['HELO', 'foo.bar.com']ようにpartsトークン化されparts 。 実際のメソッド検索名はparts[0]から取られparts[0]

(元のメソッドはstate_COMMANDとも呼ばれ、同じパターンを使用して状態マシンを実装します。つまりgetattr(self, 'state_' + self.mode) )です。


あなたがデフォルトを望むなら、辞書get(key[, default])メソッドを使うことができます:

def f(x):
    return {
        'a': 1,
        'b': 2
    }.get(x, 9)    # 9 is default if x not found

あなたが複雑なケースブロックを持っているなら、関数辞書ルックアップテーブルの使用を検討することができます...

あなたがデバッガにステップインし、どのようにディクショナリが各機能をどのようにルックアップするかを見るのが良いアイデアの前にこれをやっていないならば。

注:case / dictionaryルックアップの中で "()"を使わないでください。そうしないと、辞書/ caseブロックの作成時にそれぞれの関数が呼び出されます。 これは、ハッシュ・スタイルのルックアップを使用して各関数を1回だけ呼び出すことを覚えているためです。

def first_case():
    print "first"

def second_case():
    print "second"

def third_case():
    print "third"

mycase = {
'first': first_case, #do not use ()
'second': second_case, #do not use ()
'third': third_case #do not use ()
}
myfunc = mycase['first']
myfunc()

ここの答えのほとんどはかなり古く、特に受け入れられたものなので、更新する価値があるようです。

最初に、公式のPython FAQがこれをカバーしています。単純なケースの場合はelifチェーンを、大規模または複雑なケースの場合はelifを推奨します。 また、いくつかのケースでvisit_メソッド(多くのサーバーフレームワークで使用されるスタイル)のセットが提案されています。

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

FAQにはPEP 275が記載されています.PEP 275は 、Cスタイルのswitch文の追加に関する正式な決定を得るために書かれたものです。 しかし、そのPEPは実際にはPython 3に引き継がれていましたが、 PEP 3103という別の提案として正式に拒絶されました。 答えはもちろんですが、理由や歴史に興味がある場合は、2人のPEPに追加情報へのリンクがあります。

1つのことは何回も出てきました(PEP 275で見ることができますが、これは実際の推奨事項ではありませんでしたが)。実際には4行を扱うコードが8行で、あなたがCやBashで持っている行は、いつでも書くことができます:

if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')

これはPEP 8によってはっきりと勧められているわけではありませんが、読解可能であり、非単調ではありません。

PEP 3103が却下されてから10年以上が経つにつれて、Cスタイルのケースステートメントの問題、あるいはGoのやや強力なバージョンの問題も死んだとみなされました。 誰かがpython-ideasや-devでそれを呼び出すと、古い決定に言及します。

しかし、完全なMLスタイルのパターンマッチングの考え方は、特にSwiftやRustのような言語が採用しているため、数年ごとに発生します。 問題は、代数的データ型なしではパターンマッチングを大いに利用することが難しいことです。 Guidoはこの考えに同感していますが、誰もPythonにうまく収まる提案は出ていません。 これは、3.7のdataclassやsum型を処理するためのより強力なenum型、またはさまざまな種類のステートメントローカルバインディング( PEP 3150などのさまざまな提案)の散発的な提案によって変わる可能性があります。または現在提案されている提案のセット)を含む。 しかし、これまでのところ、それはありませんでした。

Perl 6スタイルのマッチングの提案もあります。これは、基本的にelifからregex、単一ディスパッチ型切り替えに至るまでのすべてのことです。


ケーススイート内で構文の強調表示を失う心配がない場合は、次の操作を実行できます。

exec {
    1: """
print ('one')
""", 
    2: """
print ('two')
""", 
    3: """
print ('three')
""",
}.get(value, """
print ('None')
""")

value値はどこですか。Cでは、これは次のようになります。

switch (value) {
    case 1:
        printf("one");
        break;
    case 2:
        printf("two");
        break;
    case 3:
        printf("three");
        break;
    default:
        printf("None");
        break;
}

これを行うヘルパー関数を作成することもできます:

def switch(value, cases, default):
    exec cases.get(value, default)

ですから、1つ、2つ、3つの例では、次のように使用できます。

switch(value, {
    1: """
print ('one')
    """, 
    2: """
print ('two')
    """, 
    3: """
print ('three')
    """,
}, """
print ('None')
""")

値を返すだけでなく、オブジェクト上の何かを変更するメソッドを使用したいとします。 ここに記載されているアプローチを使用すると:

result = {
  'a': obj.increment(x),
  'b': obj.decrement(x)
}.get(value, obj.default(x))

ここで起こるのは、Pythonが辞書内のすべてのメソッドを評価するということです。 したがって、値が 'a'であっても、オブジェクトはxだけ増減します。

溶液:

func, args = {
  'a' : (obj.increment, (x,)),
  'b' : (obj.decrement, (x,)),
}.get(value, (obj.default, (x,)))

result = func(*args)

関数とその引数を含むリストが得られます。 この方法では、関数ポインタと引数リストだけが返され、評価されません 。 'result'は返された関数呼び出しを評価します。


定義:

def switch1(value, options):
  if value in options:
    options[value]()

ケースをマップにまとめて、かなり単純な構文を使用できます。

def sample1(x):
  local = 'betty'
  switch1(x, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye," + local),
      print("!")),
    })

私は "ラムダ"を取り除く方法でスイッチを再定義しようとし続けたが、あきらめた。定義を調整する:

def switch(value, *maps):
  options = {}
  for m in maps:
    options.update(m)
  if value in options:
    options[value]()
  elif None in options:
    options[None]()

複数のケースを同じコードにマップし、デフォルトのオプションを指定することができました:

def sample(x):
  switch(x, {
    _: lambda: print("other") 
    for _ in 'cdef'
    }, {
    'a': lambda: print("hello"),
    'b': lambda: (
      print("goodbye,"),
      print("!")),
    None: lambda: print("I dunno")
    })

複製された各ケースは、それ自身の辞書になければなりません。switch()は、値を参照する前に辞書を統合します。まだ醜いですが、すべてのキーをループするのではなく、式にハッシュされた参照を使用する基本的な効率があります。


私が使用するソリューション:

ここに掲載された2つのソリューションの組み合わせ。比較的読みやすく、デフォルトをサポートしています。

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}.get(whatToUse, lambda x: x - 22)(value)

どこで

.get('c', lambda x: x - 22)(23)

dictで"lambda x: x - 2"を検索し、 x=23使用する

.get('xxx', lambda x: x - 22)(44)

dictでそれを見つけられず、デフォルトの"lambda x: x - 22" x=44ます。


私が使用する傾向のある解決策は、辞書を使用することです:

def decision_time( key, *args, **kwargs):
    def action1()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action2()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action3()
        """This function is a closure - and has access to all the arguments"""
        pass

   return {1:action1, 2:action2, 3:action3}.get(key,default)()

これは、毎回関数を評価しようとせず、内部関数が必要とするすべての情報を外部関数が確実に取得するようにするだけでよいという利点があります。


私の好きなものは本当に素晴らしいrecipeです。 あなたは本当にそれを好きになるでしょう。 これは実際のスイッチケースステートメント、特に機能の中で最もよく見かけるものです。

ここに例があります:

# The following example is pretty much the exact use-case of a dictionary,
# but is included for its simplicity. Note that you can include statements
# in each suite.
v = 'ten'
for case in switch(v):
    if case('one'):
        print 1
        break
    if case('two'):
        print 2
        break
    if case('ten'):
        print 10
        break
    if case('eleven'):
        print 11
        break
    if case(): # default, could also just omit condition or 'if True'
        print "something else!"
        # No need to break here, it'll stop anyway

# break is used here to look as much like the real thing as possible, but
# elif is generally just as good and more concise.

# Empty suites are considered syntax errors, so intentional fall-throughs
# should contain 'pass'
c = 'z'
for case in switch(c):
    if case('a'): pass # only necessary if the rest of the suite is empty
    if case('b'): pass
    # ...
    if case('y'): pass
    if case('z'):
        print "c is lowercase!"
        break
    if case('A'): pass
    # ...
    if case('Z'):
        print "c is uppercase!"
        break
    if case(): # default
        print "I dunno what c was!"

# As suggested by Pierre Quentel, you can even expand upon the
# functionality of the classic 'case' statement by matching multiple
# cases in a single shot. This greatly benefits operations such as the
# uppercase/lowercase example above:
import string
c = 'A'
for case in switch(c):
    if case(*string.lowercase): # note the * for unpacking as arguments
        print "c is lowercase!"
        break
    if case(*string.uppercase):
        print "c is uppercase!"
        break
    if case('!', '?', '.'): # normal argument passing style also applies
        print "c is a sentence terminator!"
        break
    if case(): # default
        print "I dunno what c was!"

私はいつもこのようにしたがっていた

result = {
  'a': lambda x: x * 5,
  'b': lambda x: x + 7,
  'c': lambda x: x - 2
}[value](x)

ここから


私はこれに対して比較的柔軟で再利用可能な解決策を作りました。 この要点としてGitHubで見つかることができます。 スイッチ関数の結果が呼び出し可能な場合、自動的に呼び出されます。


私はそれが共通のスイッチ構造であることを発見しました。

switch ...parameter...
case p1: v1; break;
case p2: v2; break;
default: v3;

Pythonで次のように表現することができます:

(lambda x: v1 if p1(x) else v2 if p2(x) else v3)

より明確な方法でフォーマットされます。

(lambda x:
     v1 if p1(x) else
     v2 if p2(x) else
     v3)

文ではなく、Pythonのバージョンは式であり、値に評価されます。


私はマーク・ビーズの答えが好きだった

x変数は2回使用する必要があるため、ラムダ関数をパラメータなしに変更しました。

私はresults[value](value)実行する必要があります

In [2]: result = {
    ...:   'a': lambda x: 'A',
    ...:   'b': lambda x: 'B',
    ...:   'c': lambda x: 'C'
    ...: }
    ...: result['a']('a')
    ...: 
Out[2]: 'A'

In [3]: result = {
    ...:   'a': lambda : 'A',
    ...:   'b': lambda : 'B',
    ...:   'c': lambda : 'C',
    ...:   None: lambda : 'Nothing else matters'

    ...: }
    ...: result['a']()
    ...: 
Out[3]: 'A'

編集:私は辞書とNoneタイプを使用することができます気づいた。 これはswitch ; case elseをエミュレートしswitch ; case else switch ; case else


私は答えを読んだ後、かなり混乱しましたが、これですべてがクリアされました:

def numbers_to_strings(argument):
    switcher = {
        0: "zero",
        1: "one",
        2: "two",
    }
    return switcher.get(argument, "nothing")

このコードは次のようなものです。

function(argument){
    switch(argument) {
        case 0:
            return "zero";
        case 1:
            return "one";
        case 2:
            return "two";
        default:
            return "nothing";
    }
}

関数への辞書マッピングの詳細については、Sourceを確認してください。


Greg Hewgillの答えを展開する- デコレータを使って辞書ソリューションをカプセル化することができます:

def case(callable):
    """switch-case decorator"""
    class case_class(object):
        def __init__(self, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs

        def do_call(self):
            return callable(*self.args, **self.kwargs)

return case_class

def switch(key, cases, default=None):
    """switch-statement"""
    ret = None
    try:
        ret = case[key].do_call()
    except KeyError:
        if default:
            ret = default.do_call()
    finally:
        return ret

これを@case-decorator と一緒に使うことができます

@case
def case_1(arg1):
    print 'case_1: ', arg1

@case
def case_2(arg1, arg2):
    print 'case_2'
    return arg1, arg2

@case
def default_case(arg1, arg2, arg3):
    print 'default_case: ', arg1, arg2, arg3

ret = switch(somearg, {
    1: case_1('somestring'),
    2: case_2(13, 42)
}, default_case(123, 'astring', 3.14))

print ret

良いニュースは、これがすでにNeoPySwitchモジュールで行われていることです。単にpipを使用してインストールしてください:

pip install NeoPySwitch

このすばらしい答えに触発されました。外部コードは不要です。未検証。 落ちることが正しく機能しません。

for case in [expression]:
    if case == 1:
        do_stuff()
        # Fall through

    # Doesn't fall through INTO the later cases
    if case in range(2, 5):
        do_other_stuff()
        break

    do_default()

class Switch:
    def __init__(self, value): self._val = value
    def __enter__(self): return self
    def __exit__(self, type, value, traceback): return False # Allows traceback to occur
    def __call__(self, *mconds): return self._val in mconds

from datetime import datetime
with Switch(datetime.today().weekday()) as case:
    if case(0):
        # Basic usage of switch
        print("I hate mondays so much.")
        # Note there is no break needed here
    elif case(1,2):
        # This switch also supports multiple conditions (in one line)
        print("When is the weekend going to be here?")
    elif case(3,4): print("The weekend is near.")
    else:
        # Default would occur here
        print("Let's go have fun!") # Didn't use case for example purposes

class switch(object):
    value = None
    def __new__(class_, value):
        class_.value = value
        return True

def case(*args):
    return any((arg == switch.value for arg in args))

使用法:

while switch(n):
    if case(0):
        print "You typed zero."
        break
    if case(1, 4, 9):
        print "n is a perfect square."
        break
    if case(2):
        print "n is an even number."
    if case(2, 3, 5, 7):
        print "n is a prime number."
        break
    if case(6, 8):
        print "n is an even number."
        break
    print "Only single-digit numbers are allowed."
    break

テスト:

n = 2
#Result:
#n is an even number.
#n is a prime number.
n = 11
#Result:
#Only single-digit numbers are allowed.




switch-statement