python - for - switch case 예외 처리



파이썬에서 switch 문을 대신할까요? (20)

"스위치로 사전"아이디어를 확대했다. 스위치에 기본값을 사용하려면 다음을 수행하십시오.

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

파이썬에서 입력 인덱스의 값을 기반으로 다른 고정 값을 반환하는 함수를 작성하고 싶습니다.

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


Answer #1

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' 받을 수 있습니다. 이것은 ['HELO', 'foo.bar.com'] 와 같이 parts 으로 토큰 화됩니다. 실제 메서드 조회 이름은 parts[0] 에서 가져옵니다.

(원래의 메소드는 state_COMMAND 라고도 state_COMMAND . 왜냐하면 동일한 패턴을 사용하여 상태 머신을 구현하기 때문입니다. 즉, getattr(self, 'state_' + self.mode) )


Answer #2

그냥 값을 반환하고 싶지는 않지만 객체의 무언가를 변경하는 메서드를 사용하려고한다고 가정 해 보겠습니다. 여기에 명시된 접근 방식을 사용하면 :

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'는 리턴 된 함수 호출을 평가합니다.


Answer #3

기본값을 원한다면 사전 get(key[, default]) 메서드를 사용할 수 있습니다 :

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

Answer #4

나는 Mark Bies의 대답을 좋아했다.

x 변수가 두 번 사용되어야하기 때문에, 나는 람다 함수를 parameterless로 수정했다.

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


Answer #5

나는 여기에 두 센트를 떨어 뜨릴거야. 파이썬에서 case / switch 문이 존재하지 않는 이유는 파이썬이 'Theres 오직 한 가지 올바른 방법'의 원칙을 따르기 때문입니다. 그래서 스위치 / 케이스 기능을 재창조하는 다양한 방법을 생각해 낼 수는 있지만,이를 달성하는 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에서 우리에게 제시 한 원칙에서 비롯된 것입니다. "무언가를 할 올바른 방법은 단 하나뿐입니다."


Answer #6

나는 항상이 방법을 좋아했다.

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

여기에서


Answer #7

내가 사용하는 솔루션 :

여기에 게시 된 솔루션 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에서 찾지 않고 x=44 기본 "lambda x: x - 22" 사용합니다.


Answer #8

당신은 사전을 사용할 수 있습니다 :

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

Answer #9

복잡한 사례 블록이있는 경우 함수 사전 조회 테이블 사용을 고려할 수 있습니다.

디버거에 들어가서 사전이 어떻게 각 기능을 찾는지를보기 전에이 작업을하지 않았다면.

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

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

Answer #10

스위치 / 케이스에 대한 내가 가장 좋아하는 파이썬 방법은 다음과 같습니다.

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

Answer #11

여기에있는 대부분의 대답은 꽤 오래된 것이며, 특히 받아 들여지는 것이므로 업데이트 할 가치가 있습니다.

첫째, 공식 Python FAQ에서 다루고 있으며, 단순한 경우는 elif chain을, 크거나 더 복잡한 경우는 dict 을 추천합니다. 또한 어떤 경우에는 visit_ 메소드 (많은 서버 프레임 워크에서 사용되는 스타일) 세트를 제안합니다.

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

FAQ에는 P 스타일 스위치 구문 추가에 대한 공식적인 결정을 내리기 위해 작성된 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)과 같은 언어가이를 채택했기 때문에 그렇습니다. 문제는 대수 데이터 형식이 없으면 패턴 매칭을 많이 사용하기 어렵다는 것입니다. Guido는이 아이디어에 호의적 이었지만 아무도 Python에 잘 들어 맞는 제안을 내놓지 않았습니다. (예를 들어 나의 2014 strawman 을 읽을 수있다.) 이것은 3.7의 dataclass 와 합계 유형을 다루는 좀 더 강력한 enum 을위한 산발적 인 제안이나 다른 종류의 statement-local 바인딩 ( PEP 3150 , 또는 현재 논의중인 제안 세트). 그러나 지금까지는 그렇지 않았습니다.

Perl 6 스타일 매칭에 대한 제안도 있는데, 기본적으로 elif 에서 regex에서 single-dispatch 타입 전환에 이르기까지 모든 것을 뒤범벅으로 사용합니다.


Answer #12

최선의 방법은 코드를 테스트 할 수있게 유지하기 위해 파이썬 언어 관용구를 사용하는 것 입니다. 이전 답변에서 보았 듯이 사전을 사용 하여 파이썬 구조와 언어를 활용 하고 "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"의. 이런 식으로 남용을 피하고 깨끗하고 시험 가능한 모든 것을 유지할 수 있습니다 .

net이나 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)

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


Answer #13

나는 대답을 읽은 후에 아주 혼란 ​​스러웠다. 그러나 이것으로 모든 것을 해결했다.

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 를 확인하십시오 .


Answer #14

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

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

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


Answer #15

정의 :

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 ()는 값을 찾기 전에 사전을 통합합니다. 그것은 여전히 ​​내가 원하는 것보다 못생긴이지만 모든 키를 통한 루프가 아니라 표현식에 해시 된 조회를 사용하는 기본적인 효율성을 가지고 있습니다.


Answer #16

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

Answer #17

이 멋진 답변에서 영감을 얻었습니다 . 외부 코드가 필요 없습니다. 검증되지 않은. 넘어서 제대로 작동하지 않습니다.

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

Answer #18
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

Answer #19
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