파이썬에서 switch 문을 대체 하시겠습니까?


1718

입력 인덱스 값을 기반으로 다른 고정 값을 반환하는 함수를 Python으로 작성하고 싶습니다.

다른 언어에서는 switchor case문을 사용 하지만 Python에는 문이없는 것 같습니다 switch. 이 시나리오에서 권장되는 Python 솔루션은 무엇입니까?


77
Guido 자신에 의해 작성된 관련 PEP : PEP 3103
— chb

28
@chb 그 PEP에서 Guido는 if / elif 체인이 고전적인 오류 원인이라고 언급하지 않습니다. 매우 깨지기 쉬운 구조입니다.
— itsbruce

15
모든 솔루션에서 누락 된 사례 값 이 감지되지 않습니다. 빠른 실패 원칙으로, 이것은 성능이나 성능 저하 기능보다 더 중요한 손실 일 수 있습니다.
— Bob Stein

6
switch실제로 입력 인덱스 값에 따라 다른 고정 값을 반환하는 것보다 "다목적"입니다. 다른 코드 조각을 실행할 수 있습니다. 실제로는 값을 반환 할 필요조차 없습니다. 여기에있는 답변 중 일부가 일반적인 switch진술을 대체 하거나 일반적인 코드 조각을 실행할 가능성이없는 값을 반환하는 경우에만 적합합니다.
— sancho.s ReinstateMonicaCellio

3
@ MalikA.Rumi Fragile 구문은 while 루프를 사용하여 수행하려는 작업을 수행하는 경우 취약한 구문입니다. for 루프를 사용하는 데 약한 프로그래머를 부를 것입니까? 루프는 실제로 필요한 전부입니다. 그러나 for 루프는 명확한 의도를 나타내며 무의미한 상용구를 저장하고 강력한 추상화를 만들 수있는 기회를 제공합니다.
— itsbruce

답변:


1486

사전을 사용할 수 있습니다.

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]

100
x를 찾지 못하면 어떻게됩니까?
— Nick

46
@ 닉 : 당신은 defaultdict를 사용할 수 있습니다
— 엘리

385
퍼포먼스에 문제가있는 경우 함수 외부에 dict를 넣는 것이 좋습니다. 따라서 모든 함수 호출에서 dict을 다시 작성하지는 않습니다.
— Claudiu

56
@EliBendersky, 이 경우 get메소드를 사용하는 것이 아마도 정상적인 경우 일 것입니다 collections.defaultdict.
— Mike Graham

27
@Nick, 예외가 발생했습니다 }.get(x, default). 기본값이 있어야합니다. (참고 :이 switch 문 오프 기본값을 떠날 경우 발생하는보다 많은 좋네요!)
— 마이크 그레이엄

1375

기본값을 원하면 사전 get(key[, default])방법을 사용할 수 있습니다 .

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

11
'a'와 'b'가 1과 일치하고 'c'와 'd'가 2와 일치하면 어떻게됩니까?
— John Mee

13
@JM : 글쎄, 사전 검색은 대체를 지원하지 않습니다. 이중 사전 검색을 수행 할 수 있습니다. 즉, 'a'& 'b'는 answer1을 가리키고 'c'및 'd'는 두 번째 사전에 포함 된 answer2를 가리 킵니다.
— Nick

3
이것은 기본값을 전달하는 것이 좋습니다
— HaTiMSuM

이 방법에는 문제가 있습니다. 먼저 전화를 걸 때마다 더 복잡한 값을 가진 경우 다시 dict를 작성하려고합니다. 예를 들어 예외를 얻을 수 있습니다. x가 튜플이고 다음과 같이하고 싶다면 x = ( 'a') def f (x) : return { 'a': x [0], 'b': x [1]} .get ( x [0], 9) 이렇게하면 IndexError가 발생합니다
— Idan Haim Shalom

2
@Idan : 문제는 스위치를 복제하는 것이 었습니다. 홀수 값을 넣으려고하면이 코드를 깨뜨릴 수 있다고 확신합니다. 예, 다시 작성하지만 수정하는 것은 간단합니다.
— Nick

394

나는 항상 이런 식으로하는 것을 좋아했습니다.

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

여기에서


16
핸들 기본에 도착 ()와 함께 좋은 방법은 너무 내 최선의 선택
— drAlberT

27
람다는 실제로 사전을 만들 때마다 호출되기 때문에이 경우 람다를 사용하는 것은 좋지 않습니다.
— Asher

13
슬프게도 이것은 사람들이 가장 가까운 곳입니다. .get()현재의 최고 답변과 같은 방법을 사용하는 방법 은 디스패치 전에 모든 가능성을 열심히 평가해야하므로 매우 비효율적 일뿐만 아니라 부작용도있을 수 없습니다. 이 답변은 그 문제를 해결하지만 더 장황합니다. 나는 if / elif / else를 사용하고 심지어 'case'처럼 쓰는 데 시간이 오래 걸립니다.
— ninjagecko

13
결과 중 하나만 반환하더라도 모든 경우에 매번 모든 함수 / 람다를 평가하지 않습니까?
— slf

23
@ slf 아니오, 제어 흐름이 해당 코드에 도달하면 3 개의 함수 (3 람다를 사용하여)를 작성한 다음 해당 3 개의 함수로 값을 사전으로 작성하지만 호출되지는 않습니다 ( 평가 는 약간 모호합니다. 그 맥락에서) 처음에. 그런 다음 사전을 통해 색인을 생성 [value]하여 3 개의 기능 중 하나만 반환합니다 ( value3 개의 키 중 하나 라고 가정 ). 이 시점에서 함수가 아직 호출되지 않았습니다. 그런 다음 (x)방금 리턴 한 함수를 x인수 로 호출합니다 (결과는로 이동 함 result). 다른 두 함수는 호출되지 않습니다.
— blubberdiblub

354

(정말 BTW, 등), 당신은 또한 사용할 수있는 사전 방법뿐만 아니라 if- elif- else얻기 위해 switch/ case/ default기능 :

if x == 'a':
    # Do the thing
elif x == 'b':
    # Do the other thing
if x in 'bc':
    # Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
    # Do yet another thing
else:
    # Do the default

이것은 물론 스위치 / 케이스와 동일하지 않습니다. break문장 을 빠뜨리는 것처럼 쉽게 넘어 질 수는 없지만 더 복잡한 테스트를 할 수 있습니다. if기능적으로 그것이 더 가깝더라도 형식은 일련의 중첩 된 것보다 좋습니다 .


51
나는 이것을 정말로 선호한다. 그것은 표준 언어 구조를 사용하며, 일치하는 케이스가 없다면 KeyError를 던지지 않는다
— martyglaubitz

7
사전 / get방식 에 대해 생각 했지만 표준 방식은 더 읽기 쉽습니다.
— Martin Thoma

2
@someuser 그러나 그들이 "겹칠"수 있다는 사실은 기능입니다. 당신은 순서가 일치하는 우선 순위를 확인하십시오. 반복되는 x에 관해서는 : 그냥 x = the.other.thing전에하십시오. 일반적으로 이해하기 쉽기 때문에 하나의 if, 여러 elif 및 다른 하나를 갖습니다.
— Matthew Schinckel

7
그래도 "elif를 사용하지 않는 방식"은 약간 혼란 스럽다. 이건 어때 : "통과"를 잊고 그냥 2로 받아 if/elif/else들입니까?
— Alois Mahdal

7
사용하는 것을 좋아하는 경우 또한 언급 할만큼 가치, x in 'bc'명심 "" in "bc"이다 True.
— Lohmar ASHAR

185

스위치 / 케이스에서 가장 좋아하는 Python 레시피는 다음과 같습니다.

choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')

간단한 시나리오를 위해 짧고 간단합니다.

11 줄 이상의 C 코드와 비교하십시오.

// C Language version of a simple 'switch/case'.
switch( key ) 
{
    case 'a' :
        result = 1;
        break;
    case 'b' :
        result = 2;
        break;
    default :
        result = -1;
}

튜플을 사용하여 여러 변수를 지정할 수도 있습니다.

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}
(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))

16
나는 이것이 허용 된 것보다 더 강력한 대답이라고 생각합니다.
— cerd

3
@some user : C는 모든 경우에 반환 값이 동일한 유형이어야합니다. 파이썬은 그렇지 않습니다. 누군가가 그러한 사용을 보증하는 상황이있는 경우를 대비하여 파이썬의 유연성을 강조하고 싶었습니다.
— ChaimG

3
@ 일부 사용자 : 개인적으로 {} .get (,)을 읽을 수 있습니다. 파이썬 초보자를위한 가독성을 높이기 위해 사용할 수 있습니다 default = -1; result = choices.get(key, default).
— ChaimG

4
c ++의 한 줄과 비교result=key=='a'?1:key==b?2:-1
— Jasen

4
@Jasen은 한 줄의 Python으로도 할 수 있다고 주장 할 수 있습니다 result = 1 if key == 'a' else (2 if key == 'b' else 'default'). 그러나 하나의 라이너는 읽을 수 있습니까?
— ChaimG

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

64
이것은 위협 안전하지 않습니다. 여러 스위치가 동시에 맞으면 모든 스위치가 마지막 스위치의 값을 갖습니다.
— francescortiz

48
@francescortiz는 스레드 안전을 의미하지만 위협 안전은 아닙니다. 변수의 가치를 위협합니다!
— Zizouz212

7
스레드 안전성 문제는 스레드 로컬 스토리지를 사용하여 해결할 수 있습니다 . 또는 인스턴스를 반환하고 사례 비교를 위해 해당 인스턴스를 사용하면 피할 수 있습니다.
— blubberdiblub

6
@blubberdiblub 그러나 표준 if문장 을 사용하는 것이 더 효율적이지 않습니까?
— wizzwizz4

9
여러 기능을 사용하는 경우에도 안전하지 않습니다. 주어진 예에서, case(2)블록이 switch ()를 사용하는 다른 함수를 호출 한 경우 다음을 수행 case(2, 3, 5, 7)하기 위해 etc를 수행 할 때 현재 switch 문이 설정 한 값이 아닌 다른 함수가 설정 한 스위치 값을 사용합니다. .
— user9876

52

내가 가장 좋아하는 것은 정말 좋은 요리법 입니다. 당신은 정말로 그것을 좋아할 것입니다. 실제 기능에 대해 스위치 케이스 문장에서 본 가장 가까운 것입니다.

class switch(object):
    def __init__(self, value):
        self.value = value
        self.fall = False

    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration

    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args: # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False

예를 들면 다음과 같습니다.

# 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!"

3
나는 대체 할 것 for case in switch()으로 with switch() as case, 더 의미가 한 번만 실행의 필요 때문에.
— 스키

4
@ Skirmantas : with허용하지 않으므로 대체 break옵션이 제거됩니다.
— Jonas Schäfer

5
이것을 스스로 결정하기 위해 더 많은 노력을 기울이지 않은 것에 대한 사과 : 위의 비슷한 대답은 스레드 안전하지 않습니다. 이거에요?
— David Winiecki

1
@DavidWiniecki 위에서 누락 된 코드 구성 요소 (및 아마도 activestate의 저작권)는 스레드로부터 안전합니다.
— Jasen

이것의 다른 버전은 다음과 같을 if c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"까요?
— mpag

51
class Switch:
    def __init__(self, value):
        self.value = value

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        return False # Allows a traceback to occur

    def __call__(self, *values):
        return self.value in values


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

9
컨텍스트 관리자를 사용하는 것이 좋은 창의적 솔루션입니다. 이 게시물에 컨텍스트를 제공하기 위해 약간의 설명과 컨텍스트 관리자에 대한 정보 링크를 추가하는 것이 좋습니다.)
— Will

2
나는 if / elif 체인을 좋아하지 않지만 이것은 파이썬의 기존 구문을 사용하여 본 모든 솔루션 중 가장 창의적이고 가장 실용적인 솔루션입니다.
— itsbruce

2
정말 좋습니다. 제안 된 개선 사항 중 하나는 (공용) value속성을 Switch 클래스 에 추가 case.value하여 명령문 내에서 참조 할 수 있도록하는 것 입니다.
— Peter

48

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_메소드가 있고에 전달합니다 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'). 이것은로 토큰 화 parts됩니다 ['HELO', 'foo.bar.com']. 실제 메소드 조회 이름은에서 가져옵니다 parts[0].

(원본 방법이라고도 state_COMMAND은 상태 머신을 구현하기 위해 동일한 패턴을 사용하기 때문에, 즉 getattr(self, 'state_' + self.mode))


4
SMTP (). do_HELO ( 'foo.bar.com') 메소드를 직접 호출하는 것보다이 패턴의 이점을 보지 못합니다. SMTP (). do_HELO ( 'foo.bar.com') OK, lookupMethod에 공통 코드가있을 수 있지만 덮어 쓸 수 있기 때문에 서브 클래스는 간접적으로 얻은 것을 보지 못합니다.
— Mr Shark

1
어떤 메소드를 미리 호출해야하는지 알 수 없습니다. 즉 'HELO'는 변수에서 나온 것입니다. 원래 게시물에 사용 예를 추가했습니다

나는 단순히 제안 할 수 있습니다 : 평가를 ( '. SMTP ()를 do_'+ 명령) ( 'foo.bar.com')
— jforberg

8
평가? 진심으로? 호출 당 하나의 메소드를 인스턴스화하는 대신 내부 상태가없는 경우 한 번만 인스턴스화하여 모든 호출에 사용할 수 있습니다.
— Mahesh

1
여기서 실제 키는 getattr을 사용하여 실행할 함수를 지정하는 디스패치입니다. 메소드가 모듈에있는 경우 getattr (locals (), func_name)을 사용하여 얻을 수 있습니다. 'do_'부분은 보안 / 오류에 적합하므로 접두사를 가진 기능 만 호출 할 수 있습니다. SMTP 자체는 lookupMethod를 호출합니다. 이상적으로 외부는 이것에 대해 알지 못합니다. SMTP (). lookupMethod (name) (data)를 수행하는 것은 실제로 의미가 없습니다. 명령과 데이터가 하나의 문자열에 있고 SMTP가 구문 분석하므로 더 의미가 있습니다. 마지막으로 SMTP는 아마도 다른 공유 상태를 가지고있어이를 클래스로 정당화 할 수 있습니다.
— ShawnFumo

27

단순히 값을 반환하지 않고 객체에서 무언가를 변경하는 메소드를 사용한다고 가정 해 봅시다. 여기에 명시된 접근 방식은 다음과 같습니다.

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

여기서 일어나는 일은 파이썬이 사전의 모든 메소드를 평가한다는 것입니다. 따라서 값이 'a'인 경우에도 객체는 x 씩 증가 및 감소합니다.

해결책:

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

result = func(*args)

따라서 함수와 인수가 포함 된 목록을 얻습니다. 이런 식으로 함수 포인터와 인수 목록 만 반환되고 평가 되지 는 않습니다 . 그런 다음 'result'는 반환 된 함수 호출을 평가합니다.


23

여기에 2 센트를 떨어 뜨릴 것입니다. 파이썬에 case / switch 문이없는 이유는 파이썬이 '무언가를 수행하는 단 하나의 방법이 있습니다'라는 원칙을 따르기 때문입니다. 따라서 분명히 스위치 / 케이스 기능을 재생성하는 다양한 방법을 생각 해낼 수 있지만이를 달성하는 Pythonic 방법은 if ​​/ elif 구문입니다. 즉

if something:
    return "first thing"
elif somethingelse:
    return "second thing"
elif yetanotherthing:
    return "third thing"
else:
    return "default thing"

나는 단지 PEP 8이 고개를 끄덕 여야한다고 느꼈다. 파이썬의 아름다운 점 중 하나는 단순함과 우아함입니다. 이는 PEP 8에 규정 된 원칙에서 비롯된 것입니다. "무엇을 할 수있는 올바른 방법은 하나뿐입니다"


6
그렇다면 왜 파이썬에는 for 루프와 while 루프가 있습니까? while 루프로 구현할 수있는 for 루프로 수행 할 수있는 모든 것.
— itsbruce

1
진실. 프로그래머를 시작하면 스위치 / 케이스가 너무 자주 남용됩니다. 그들이 정말로 원하는 것은 전략 패턴 입니다.
— user228395

파이썬처럼 Clojure가 좋기를 바란다
— TWR Cole

1
@TWRCole 나는 그렇게 생각하지 않는다. 파이썬이 먼저하고 있었다. 파이썬은 1990 년부터, 2007 년부터 클로저에서 활동 해왔다.
— Taylor

올바른 일을하는 유일한 방법이 있습니다. 파이썬 2.7 또는 파이썬 3? 롤
— TWR Cole

17

"스위치로 결정"아이디어를 확장합니다. 스위치에 기본값을 사용하려는 경우 :

def f(x):
    try:
        return {
            'a': 1,
            'b': 2,
        }[x]
    except KeyError:
        return 'default'

14
기본적으로 지정된 dict에서 .get ()을 사용하는 것이 더 명확하다고 생각합니다. 예외적 인 상황에서 예외를 남기는 것을 선호하며 모호하지 않고 세 줄의 코드와 들여 쓰기 수준을 줄입니다.
— Chris B.

10
이것은 이다 예외적 인 상황. 유용성에 따라 드문 상황 이 될 수도 있고 아닐 수도 있지만 'default', 규칙에서 예외 (돌아온다 ) (이 dict에서 무언가를 얻으십시오). 의도적으로 파이썬 프로그램은 모자를 쓰러 뜨릴 때 예외를 사용합니다. 말하자면, 사용 get하면 코드가 조금 더 좋을 수 있습니다.
— Mike Graham

16

복잡한 케이스 블록이 있으면 함수 사전 조회 테이블 사용을 고려할 수 있습니다 ...

이 작업을 수행하지 않은 경우 디버거로 들어가서 사전이 각 함수를 찾는 방법을 정확하게 보는 것이 좋습니다.

참고 : 사례 / 사전 조회 내에서 "()"를 사용 하지 마십시오. 그렇지 않으면 사전 / 사례 블록이 만들어 질 때 각 함수를 호출합니다. 해시 스타일 조회를 사용하여 각 함수를 한 번만 호출하기 때문에이 점을 기억하십시오.

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()

나는 당신의 해결책을 좋아합니다. 그러나 변수 나 객체를 전달해야하는 경우 어떻게해야합니까?
— 테도 브 르바 네크

메소드가 매개 변수를 예상하는 경우에는 작동하지 않습니다.
— Kulasangar

16

"switch"와 같이 추가 명령문을 검색하는 경우 Python을 확장하는 python 모듈을 빌드했습니다. ESPY 라고합니다 "파이썬에 대한 강화 된 구조"라고 그것은 파이썬 2.x 및 파이썬 3.x에서 모두 사용할 수

예를 들어,이 경우 switch 문은 다음 코드로 수행 할 수 있습니다.

macro switch(arg1):
    while True:
        cont=False
        val=%arg1%
        socket case(arg2):
            if val==%arg2% or cont:
                cont=True
                socket
        socket else:
            socket
        break

다음과 같이 사용할 수 있습니다.

a=3
switch(a):
    case(0):
        print("Zero")
    case(1):
        print("Smaller than 2"):
        break
    else:
        print ("greater than 1")

그래서 espy는 파이썬에서 다음과 같이 번역합니다.

a=3
while True:
    cont=False
    if a==0 or cont:
        cont=True
        print ("Zero")
    if a==1 or cont:
        cont=True
        print ("Smaller than 2")
        break
    print ("greater than 1")
    break

매우 멋지지만 while True:생성 된 Python 코드의 맨 위에 있는 점은 무엇 입니까? 그것은 필연적으로 충돌 것 break이 모두가 나에게 보인다 있도록, 생성 된 파이썬 코드의 하단 while True:및 break제거 할 수 있습니다. 또한 ESPY cont는 사용자가 자신의 코드에서 동일한 이름을 사용하는 경우 이름을 변경하기에 충분히 똑똑 합니까? 어쨌든, 나는 바닐라 파이썬을 사용하고 싶기 때문에 이것을 사용하지는 않지만 그럼에도 불구하고 시원합니다. 시원함을 위해 +1
— ArtOfWarfare 12

에 대한 이유를 @ArtOfWarfare while True:와 break이야하는 것은 허용하지만, 가을-을 필요로하지 않는 것입니다.
— Solomon Ucko

이 모듈을 계속 사용할 수 있습니까?
— Solomon Ucko

15

나는 일반적인 스위치 구조를 발견했다.

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

다음과 같이 파이썬으로 표현할 수 있습니다 :

(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 버전은 명령문이 아닌 표현식으로 값으로 평가됩니다.


또한 대신 ... 매개 변수 ... 그리고 P1 (x)의 방법 parameter과p1==parameter
— 밥 스타

@ BobStein-VisiBone 안녕하세요, 다음은 파이썬 세션에서 실행되는 예입니다 f = lambda x: 'a' if x==0 else 'b' if x==1 else 'c'. 내가 나중에 전화했을 때 f(2), 내가 가지고 'c'; f(1), 'b'; 와 f(0), 'a'. p1 (x)는 술어를 나타내고; 한가 반환로 True하거나 False, 더는 함수 호출 또는 표현, 그것의 괜찮 문제가 없습니다.
— leo

@ BobStein-VisiBone 네, 맞습니다! 감사합니다 :) 여러 줄로 된 표현식이 작동하려면 괄호를 제안이나 수정 된 예제와 같이 배치해야합니다.
— leo

우수한. 이제 Parens에 대한 모든 의견을 삭제 하겠습니다 .
— Bob Stein

15

여기에있는 대부분의 답변은 꽤 오래되었으며 특히 받아 들인 답변이므로 업데이트 할 가치가있는 것으로 보입니다.

첫째, 공식 Python FAQ 는이를 다루고 elif간단한 경우와 dict더 크고 복잡한 경우 의 체인을 권장합니다 . 또한 경우에 따라 일련의 visit_메소드 (많은 서버 프레임 워크에서 사용되는 스타일)를 제안합니다 .

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

이 FAQ에는 C 스타일 스위치 문 추가에 대한 공식적인 결정을 내리기 위해 작성된 PEP 275 도 언급되어 있습니다. 그러나 PEP는 실제로 Python 3으로 연기되었으며 PEP 3103 이라는 별도의 제안으로 공식적으로 거부되었습니다 . 대답은 물론 아닙니다. 그러나 두 PEP에는 이유나 기록에 관심이있는 경우 추가 정보에 대한 링크가 있습니다.


여러 번 나온 (실제 권장 사항으로 잘려도 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와 같은 언어가이를 채택했기 때문입니다. 문제는 대수 데이터 유형이 없으면 패턴 일치를 많이 사용하기가 어렵다는 것입니다. 귀도는이 아이디어에 동조했지만 파이썬에 잘 맞는 제안은 아무도 없습니다. ( 예를 들어 2014 strawman 을 읽을 수 있습니다 .) 이것은 dataclass3.7에서 변경되고 enum합계 유형을 보다 강력 하게 처리하기 위한 산발적 인 제안 이나 PEP 3150 과 같은 다양한 종류의 명령문 로컬 바인딩에 대한 다양한 제안 또는 -ideas에 대해 현재 논의중인 제안서 세트). 그러나 지금까지는 그렇지 않았습니다.

때때로 Perl 6 스타일 매칭에 대한 제안이 있는데, 이는 기본적으로 elif정규식에서 단일 디스패치 유형 전환 에 이르기까지 모든 것들의 혼란입니다 .


15

기능을 실행하기위한 솔루션 :

result = {
    'case1':     foo1, 
    'case2':     foo2,
    'case3':     foo3,
    'default':   default,
}.get(option)()

여기서 foo1 (), foo2 (), foo3 () 및 default ()는 함수입니다.


1
예, 예를 들어 변수 option == "case2"결과 = foo2 ()
— Alejandro Quintanar


예, 목적을 이해합니다. 하지만 내 관심사는 당신이 단지 원하는 경우이다 foo2()는 foo1(), foo3()그리고 default()기능은 모든 또한 시간이 오래 걸릴 수있는 것을 의미 실행하려고
— 브라이언 언더우드을

1
사전 내부에서 ()를 생략하십시오. 사용하십시오 get(option)(). 문제 해결됨.
— timgeb

1
()의 우수한 사용은 화격자 솔루션, 나는 그것을 gist.github.com/aquintanar/01e9920d8341c5c6252d507669758fe5
— Alejandro Quintanar

13

Google 검색의 어느 곳에서나 찾고있는 간단한 답변을 찾지 못했습니다. 그러나 어쨌든 알아 냈습니다. 정말 간단합니다. 게시하기로 결정했으며 다른 사람의 머리에 약간의 흠집이 생기지 않도록 할 수 있습니다. 열쇠는 단순히 "in"과 tuples입니다. 다음은 RANDOM 폴 스루를 포함하여 폴 스루를 사용한 스위치 문 동작입니다.

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.

여기서 정확히 넘어지는 부분은 무엇입니까?
— Jonas Schäfer

죄송합니다! 거기에는 가을이 있지만 더 이상 스택 오버플로에 기여하지 않습니다. 전혀 마음에 들지 않습니다. 나는 다른 사람들의 기여를 좋아하지만 Stackoverflow는 아닙니다. FUNCTIONALITY를 위해 fall through를 사용하는 경우 스위치의 break 문에 도달 할 때까지 스위치의 한 명령문에서 모두 특정 조건을 캐치하려고합니다 (모두 캐치).
— JD Graham

2
여기에서 "Dog"및 "Cat"값이 모두 FALL THROUGH이며 SAME 기능에 의해 처리됩니다. 이는 "4 개의 레그"를 갖는 것으로 정의됩니다. 파단이 발생하는 동일한 사례 및 SAME 사례 문에서 처리하는 다른 값과 동등한 ABSTRACT입니다.
— JD Graham

@JDGraham 나는 Jonas가 폴스 오버의 또 다른 측면을 의미한다고 생각합니다. 프로그래머가 때때로 break코드 끝에 쓰는 것을 잊었을 때 발생 합니다 case. 그러나 나는 우리가 필요로하지 않는 생각 과 같은 : "위해 fallthrough을"
— 미하일 Batcer

12

내가 사용하는 솔루션 :

여기에 게시 된 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)

조회 "lambda x: x - 2"DICT과 함께 사용x=23

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

dict에서 찾지 못하고 기본값 "lambda x: x - 22"을 사용합니다 x=44.


10
# simple case alternative

some_value = 5.0

# this while loop block simulates a case block

# case
while True:

    # case 1
    if some_value > 5:
        print ('Greater than five')
        break

    # case 2
    if some_value == 5:
        print ('Equal to five')
        break

    # else case 3
    print ( 'Must be less than 5')
    break

10
def f(x):
    dictionary = {'a':1, 'b':2, 'c':3}
    return dictionary.get(x,'Not Found') 
##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary

코드에 대한 간단한 설명을 포함하여 고려와는 게시 된 문제를 해결하는 방법
— 헨리 우디

좋아, 나는 지금 그것에 대한 의견을 추가했습니다.
— Vikhyat Agarwal

8

Mark Bies의 답변이 마음에 들었습니다.

x변수는 두 번 사용해야 하므로 람다 함수를 매개 변수없이 수정했습니다.

나는 함께 달려야한다 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


None 케이스는 단순히 에뮬레이션되지 result[None]()않습니까?
— Bob Stein

그렇습니다. 내 말은result = {'a': 100, None:5000}; result[None]
— guneysus

4
아무도 생각하지 않는지 확인하는 것만으로 None:작동 default:합니다.
— Bob Stein

7
def f(x):
     return 1 if x == 'a' else\
            2 if x in 'bcd' else\
            0 #default

짧고 읽기 쉽고 기본값이 있으며 조건과 반환 값 모두에서 표현식을 지원합니다.

그러나 사전이있는 솔루션보다 효율성이 떨어집니다. 예를 들어, 파이썬은 기본값을 반환하기 전에 모든 조건을 스캔해야합니다.


7

전달 된 dict을 사용할 수 있습니다.

#!/usr/bin/env python


def case1():
    print("This is case 1")

def case2():
    print("This is case 2")

def case3():
    print("This is case 3")


token_dict = {
    "case1" : case1,
    "case2" : case2,
    "case3" : case3,
}


def main():
    cases = ("case1", "case3", "case2", "case1")
    for case in cases:
        token_dict[case]()


if __name__ == '__main__':
    main()

산출:

This is case 1
This is case 3
This is case 2
This is case 1

6

테스트되지 않은 단순; break 문이없는 한, 각 조건은 독립적으로 평가됩니다. 오류는 없지만 모든 사례가 평가됩니다 (켜기위한 표현식은 한 번만 평가되지만). 예를 들어

for case in [expression]:
    if case == 1:
        print(end='Was 1. ')

    if case == 2:
        print(end='Was 2. ')
        break

    if case in (1, 2):
        print(end='Was 1 or 2. ')

    print(end='Was something. ')

지문 Was 1. Was 1 or 2. Was something. (젠장! 왜 내가? 인라인 코드 블록의 공백을 후행 할 수 없습니다) 경우 expression로 평가 1, Was 2.경우 expression평가됩니다에 2, 또는 Was something.경우에 expression평가하여 다른 뭔가.


1
글쎄, 넘어짐은 작동하지만 do_default로 이동합니다.
— syockit

5

정의 :

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("!")),
    })

나는 "lambda :"를 제거 할 수있는 방식으로 스위치를 다시 정의하려고 노력했지만 포기했다. 정의 조정 :

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 ()는 값을 찾기 전에 사전을 통합합니다. 여전히 원하는 것보다 추악하지만 모든 키를 반복하는 것이 아니라 식에서 해시 조회를 사용하는 기본 효율성이 있습니다.


5

가장 좋은 방법은 파이썬 언어 관용구를 사용하여 코드를 테스트 할 수있게하는 것 입니다. 이전 답변에서 볼 수 있듯이 사전을 사용 하여 파이썬 구조와 언어를 활용 하고 "case"코드를 다른 방법으로 격리시킵니다. 아래에는 클래스가 있지만 모듈, 전역 및 함수를 직접 사용할 수 있습니다. 이 클래스에는 격리로 테스트 할 수있는 메소드가 있습니다 . 필요에 따라 정적 메소드 및 속성을 사용할 수도 있습니다.

class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
            "CHOICE1" : self.my_func1,
            "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

또한 "__choice_table"의 키로 클래스를 사용하여이 방법을 활용할 수 있습니다 . 이런 식으로 당신은 isinstance 남용을 피할 수 있습니다 모든 청결하고 테스트 가능한 상태를 유지할 .

네트 또는 MQ에서 많은 메시지 또는 패킷을 처리해야한다고 가정하십시오. 모든 패킷에는 고유 한 구조와 관리 코드가 있습니다 (일반적인 방식). 위의 코드를 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

따라서 코드 흐름에는 복잡성이 확산되지 않지만 코드 구조로 렌더링됩니다 .


정말 못생긴 ... 읽을 때 스위치 케이스가 너무 깨끗합니다. 파이썬으로 구현되지 않은 이유를 이해할 수 없습니다.
— jmcollin92

@AndyClifton : 죄송합니다 ... 예를 들어? 여러 의사 결정 분기 코드가 필요할 때마다이 방법을 적용 할 수 있습니다.
— J_Zar

@ jmcollin92 : 스위치 문이 편안합니다. 그러나 프로그래머는 재사용 할 수없는 매우 긴 명령문과 코드를 작성하는 경향이 있습니다. 설명 된 방법은 테스트하기에 더 깨끗하고 재사용이 가능합니다 (IMHO).
— J_Zar

@J_Zar : re. 예에 대한 나의 요청 : 예, 나는 그것을 얻지 만 더 큰 코드의 맥락에 이것을 놓고 고심하고 있습니다. 실제 상황에서 어떻게 사용할 수 있는지 보여 주실 수 있습니까?
— Andy Clifton

1
@AndyClifton : 죄송합니다. 늦었지만 사례를 게시했습니다.
— J_Zar

5

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

5

사전을 사용하는 솔루션은 다음과 같습니다.

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)()

이것은 매번 함수를 평가하려고하지 않는 장점이 있으며, 외부 함수가 내부 함수에 필요한 모든 정보를 얻도록해야합니다.


5

지금까지 많은 답변이있었습니다. "우리는 파이썬에는 스위치가 없습니다. 이런 식으로하세요." 그러나 switch 문 자체는 게으른 프로그래밍을 장려하기 때문에 대부분의 경우 피할 수 있고 피해야 할 쉽게 모욕적 인 구조라고 지적하고 싶습니다. 지목 사항:

def ToUpper(lcChar):
    if (lcChar == 'a' or lcChar == 'A'):
        return 'A'
    elif (lcChar == 'b' or lcChar == 'B'):
        return 'B'
    ...
    elif (lcChar == 'z' or lcChar == 'Z'):
        return 'Z'
    else:
        return None        # or something

이제 switch-statement (Python에서 제공 한 경우) 로이 작업을 수행 할 수 는 있지만이 작업을 수행하는 방법이 있기 때문에 시간을 낭비하고 있습니다. 아니면 덜 분명한 것이 있습니다.

def ConvertToReason(code):
    if (code == 200):
        return 'Okay'
    elif (code == 400):
        return 'Bad Request'
    elif (code == 404):
        return 'Not Found'
    else:
        return None

그러나 이러한 종류의 연산은 더 빠르고 덜 복잡하고 오류가 적으며 더 작기 때문에 사전으로 처리 할 수 ​​있으며 처리해야합니다.

스위치 문에 대한 대부분의 "사용 사례"는이 두 경우 중 하나에 해당합니다. 당신이 당신의 문제에 대해 철저히 생각했다면 그것을 사용할 이유가 거의 없습니다.

따라서 "파이썬에서 어떻게 전환합니까?"라고 묻기보다는 "파이썬으로 전환하고 싶은 이유는 무엇입니까?" 그 이유는 종종 더 흥미로운 질문이되기 때문에 건물의 디자인에 결함이있을 수 있습니다.

이제 스위치를 절대 사용해서는 안된다는 말은 아닙니다. 상태 머신, 렉서, 파서 및 오토마타는 모두 어느 정도 사용하며 일반적으로 대칭 입력에서 시작하여 비대칭 출력으로 이동할 때 유용 할 수 있습니다. 코드에 많은 손톱이 있기 때문에 스위치를 망치로 사용하지 않아야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.