파이썬에서 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)의 방법 parameterp1==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.