Switch like context manager in Python This is self-explaining example with usage in doctests (it's not that fast as implementation with dict key-lookups, but it's a lot more readable, and don't require callables and lambdas):

class Switch(object):
    """
    Switch, simple implementation of switch statement for Python, eg:

    &gt;&gt;&gt; def test_switch(val):
    ...   ret = []
    ...   with Switch(val) as case:
    ...     if case(1, fall_through=True):
    ...       ret.append(1)
    ...     if case(2):
    ...       ret.append(2)
    ...     if case.call(lambda v: 2 &lt; v &lt; 4):
    ...       ret.append(3)
    ...     if case.call(lambda v: 3 &lt; v &lt; 5, fall_through=True):
    ...       ret.append(4)
    ...     if case(5):
    ...       ret.append(5)
    ...     if case.default:
    ...       ret.append(6)
    ...   return ret
    ...
    &gt;&gt;&gt; test_switch(1)
    [1, 2]

    &gt;&gt;&gt; test_switch(2)
    [2]

    &gt;&gt;&gt; test_switch(3)
    [3]

    &gt;&gt;&gt; test_switch(4)
    [4, 5]

    &gt;&gt;&gt; test_switch(5)
    [5]

    &gt;&gt;&gt; test_switch(7)
    [6]


    &gt;&gt;&gt; def test_switch_default_fall_through(val):
    ...   ret = []
    ...   with Switch(val, fall_through=True) as case:
    ...     if case(1):
    ...       ret.append(1)
    ...     if case(2):
    ...       ret.append(2)
    ...     if case.call(lambda v: 2 &lt; v &lt; 4):
    ...       ret.append(3)
    ...     if case.call(lambda v: 3 &lt; v &lt; 5, fall_through=False):
    ...       ret.append(4)
    ...     if case(5):
    ...       ret.append(5)
    ...     if case.default:
    ...       ret.append(6)
    ...   return ret
    ...
    &gt;&gt;&gt; test_switch_default_fall_through(1)
    [1, 2, 3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(2)
    [2, 3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(3)
    [3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(4)
    [4]

    &gt;&gt;&gt; test_switch_default_fall_through(5)
    [5]

    &gt;&gt;&gt; test_switch_default_fall_through(7)
    [6]
    """

    class StopExecution(Exception):
        pass

    def __init__(self, test_value, fall_through=False):
        self._value = test_value
        self._fall_through = None
        self._default_fall_through = fall_through
        self._use_default = True
        self._default_used = False

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is self.StopExecution:
            return True

        return False

    def __call__(self, expr, fall_through=None):
        return self.call(lambda v: v == expr, fall_through)

    def call(self, call, fall_through=None):
        if self._default_used:
            raise SyntaxError('Case after default is prohibited')

        if self._finished:
            raise self.StopExecution()
        elif call(self._value) or self._fall_through:
            self._use_default = False
            if fall_through is None:
                self._fall_through = self._default_fall_through
            else:
                self._fall_through = fall_through
            return True

        return False

    @property
    def default(self):
        if self._finished:
            raise self.StopExecution()

        self._default_used = True

        if self._use_default:
            return True

        return False

    @property
    def _finished(self):
        return self._use_default is False and self._fall_through is False


class CSwitch(Switch):
    """
    CSwitch is a shortcut to call Switch(test_value, fall_through=True)
    """
    def __init__(self, test_value):
        super(CSwitch, self).__init__(test_value, fall_through=True)
I like your trick to create this syntactic sugar.  The implementation is also pretty good, as are the doctests.

Feature suggestions

I think it would be nice if a case() could test for multiple values.   A case('jack', 'queen', 'king') should match if the Switch was created with any of those three strings.

It would also be nice if there were a case.match() that performed a regular expression match.

Minor issues


If execution ends up inside case.call() due to fall-through, then I would expect the test function not to be called at all, as a kind of short-circuiting behaviour.  Specifically,

elif call(self._value) or self._fall_through:


should be reversed and written as

elif self._fall_through or call(self._value):

Having a parameter named call when the method is also named call is confusing.  I suggest renaming the parameter to test.
Avoid testing variables for equality with True and False explicitly.  Just use boolean expressions.  For example, in __exit__(), change

def __exit__(exc_type, exc_val, exc_tb):
    if exc_type is self.StopExecution:
        return True
    return False


to

def __exit__(exc_type, exc_val, exc_tb):
    return exc_type is self.StopExecution

Initialize _fall_through to False instead of None; it's slightly more informative.
Rename expr → case_value.  Rename _value → _switch_value.
Rename / invert _use_default to not _matched_case, because _use_default is too confusingly similar to _default_used.  Also, by inverting the logic, all three private variables can be initialized to False, which is more elegant.
Instead of a _finished property, write a _check_finished() method that raises StopException too.


Proposed solution

import re

class Switch(object):
    """
    Switch, simple implementation of switch statement for Python, eg:

    &gt;&gt;&gt; def test_switch(val):
    ...   ret = []
    ...   with Switch(val) as case:
    ...     if case(1, fall_through=True):
    ...       ret.append(1)
    ...     if case.match('2|two'):
    ...       ret.append(2)
    ...     if case.call(lambda v: 2 &lt; v &lt; 4):
    ...       ret.append(3)
    ...     if case.call(lambda v: 3 &lt; v &lt; 5, fall_through=True):
    ...       ret.append(4)
    ...     if case(5, 10):
    ...       ret.append('5 or 10')
    ...     if case.default:
    ...       ret.append(6)
    ...   return ret
    ...
    &gt;&gt;&gt; test_switch(1)
    [1, 2]

    &gt;&gt;&gt; test_switch(2)
    [2]

    &gt;&gt;&gt; test_switch(3)
    [3]

    &gt;&gt;&gt; test_switch(4)
    [4, '5 or 10']

    &gt;&gt;&gt; test_switch(5)
    ['5 or 10']

    &gt;&gt;&gt; test_switch(10)
    ['5 or 10']

    &gt;&gt;&gt; test_switch(7)
    [6]


    &gt;&gt;&gt; def test_switch_default_fall_through(val):
    ...   ret = []
    ...   with Switch(val, fall_through=True) as case:
    ...     if case(1):
    ...       ret.append(1)
    ...     if case(2):
    ...       ret.append(2)
    ...     if case.call(lambda v: 2 &lt; v &lt; 4):
    ...       ret.append(3)
    ...     if case.call(lambda v: 3 &lt; v &lt; 5, fall_through=False):
    ...       ret.append(4)
    ...     if case(5):
    ...       ret.append(5)
    ...     if case.default:
    ...       ret.append(6)
    ...   return ret
    ...
    &gt;&gt;&gt; test_switch_default_fall_through(1)
    [1, 2, 3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(2)
    [2, 3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(3)
    [3, 4]

    &gt;&gt;&gt; test_switch_default_fall_through(4)
    [4]

    &gt;&gt;&gt; test_switch_default_fall_through(5)
    [5]

    &gt;&gt;&gt; test_switch_default_fall_through(7)
    [6]
    """

    class StopExecution(Exception):
        pass

    def __init__(self, switch_value, fall_through=False):
        self._switch_value = switch_value
        self._default_fall_through = fall_through
        self._fall_through = False
        self._matched_case = False
        self._default_used = False

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        return exc_type is self.StopExecution

    def __call__(self, case_value, *case_values, **kwargs):
        def test(switch_value):
            return any(switch_value == v for v in (case_value,) + case_values)
        return self.call(test, **kwargs)

    def call(self, test, fall_through=None):
        if self._default_used:
            raise SyntaxError('Case after default is prohibited')

        self._check_finished()
        if self._fall_through or test(self._switch_value):
            self._matched_case = True
            self._fall_through = fall_through if fall_through is not None else self._default_fall_through
            return True

        return False

    def match(self, regex, fall_through=None):
        if self._default_used:
            raise SyntaxError('Match after default is prohibited')

        self._check_finished()
        if isinstance(regex, str):
            regex = re.compile(regex)
        if self._fall_through or regex.match(str(self._switch_value)):
            self._matched_case = True
            self._fall_through = fall_through if fall_through is not None else self._default_fall_through
            return True

        return False

    @property
    def default(self):
        self._check_finished()
        self._default_used = True
        return not self._matched_case

    def _check_finished(self):
        if self._matched_case and not self._fall_through:
            raise self.StopExecution()


class CSwitch(Switch):
    """
    CSwitch is a shortcut to call Switch(switch_value, fall_through=True)
    """
    def __init__(self, switch_value):
        super(CSwitch, self).__init__(switch_value, fall_through=True)