I don't want to discourage you too much, but I think that adding a switch statement comes *very* low on the list of improvements we would like to make in Python 3.5.
We should probably focus on speed (or aspects of it, like startup), language features that help porting Python 2 code to it (e.g. bytes formatting), and things that improve the user experience of getting started with Python on a new machine (e.g. pip/venv).. Or perhaps stdlib issues like an asyncio-infused variation of WSGI.
I've probably missed a few focus areas, but I still very much doubt we'll be adding a switch statement -- it's a "sexy" language design issue (like anonymous functions) but that's not what will help Python compete.
On 17 April 2014 23:41, Andrew Barnert abarnert-at-yahoo.com |python-ideas-at-python.org| <3en9k...@sneakemail.com> wrote:
Any reason to use the keywords that only exist in C-family languages when the functionality isn't actually like C?
> - if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr
> - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
While you can figure out what this means with a bit of work, it seems to add a lot of conceptual complexity, and I can't think of any other language that does this.
why not consider features that other languages do have, like pattern matching cases?
if you want to match (1, 'b') you'd have to write case ((1, 'b'),). Which is bad for static cases, and even worse for dynamic ones.
On 17 April 2014 23:41, Andrew Barnert abarnert-at-yahoo.com |python-ideas-at-python.org| <3en9k...@sneakemail.com> wrote:
Any reason to use the keywords that only exist in C-family languages when the functionality isn't actually like C?Well, it's the same for the "for" statement.
> - if case_expr is not a tuple, the case suite will be executed if switch_expr == case_exprWhile you can figure out what this means with a bit of work, it seems to add a lot of conceptual complexity, and I can't think of any other language that does this.
> - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
Bash case statement is similar.
IMHO this syntax is very simple, simpler than C-like switch statements. Why do you think it is complex?
why not consider features that other languages do have, like pattern matching cases?I do not know much about pattern matching.
Using my little knowledge, Python have recursive functions and regular expressions.
You could also use generator expressions with my switch statement proposal.
Symbolic pattern is powerful, but it's a bit too much for this little proposal.
if you want to match (1, 'b') you'd have to write case ((1, 'b'),). Which is bad for static cases, and even worse for dynamic ones.Yes, it's not so elegant, but I do not think that it will really matters in real world usage.
What do you mean exactly with "dynamic cases"?
case foo():
would have to become
case (foo(),):
to work as expected when foo() returned a tuple, which would mean
wrapping things in 1-tuples whenever you wanted to reliably match a
case that is determined dynamically.
It may be just me, but I fail - in a complete manner - to see how this
syntax can offer any
improvement on current if/elif chains. It seems to differ from that
only by reducing
the explicitness, and the flexibility of the test condition that can
be used in any
of the subconditions
No it isn't. First, the "for ... in" keywords are not the same as just "for" and a bunch of parens and semicolons.
Also, notice that if you try to read the switch statement, or your Python version, as English, it's nonsense.
Bash doesn't have separate "case" and "elcase" cases. After one case is done, the rest are skipped, just as in most other languages.
I don't see how skipping over any elcase but falling through to the next case is in any way simpler than C.
Well, then at least look at the limited form of pattern matching Python has in the for and assignment statements and parameter matching, and maybe look at how pattern matching is used with case statements in other languages; don't try to suggest language designs based on guesses.
... and? Are you suggesting that if the switch expression is a string and the case expression a compiled regex you could automatically call match instead of testing for equality? If not, how is having regexp even relevant here? And how are recursive functions relevant?
A generator expression is equal to anything except itself, and doesn't contain anything.
I don't know what you mean by "symbolic pattern" here.
On 18 April 2014 18:51, Andrew Barnert abarnert-at-yahoo.com |python-ideas-at-python.org| <3en9k...@sneakemail.com> wrote:
No it isn't. First, the "for ... in" keywords are not the same as just "for" and a bunch of parens and semicolons.My propose has elcase, that is not present in other languages.
Also, notice that if you try to read the switch statement, or your Python version, as English, it's nonsense.Yes, the fact a case will behave differently for tuples and non-tuples will be difficult to translate in English.
Bash doesn't have separate "case" and "elcase" cases. After one case is done, the rest are skipped, just as in most other languages.But it has ;& and ;;& similarly to break and continue of C, that it's equivalent to case and elcase of my proposal.
I don't see how skipping over any elcase but falling through to the next case is in any way simpler than C.Well, because it's coherent with if-elif. See my last example in my first message.
Well, then at least look at the limited form of pattern matching Python has in the for and assignment statements and parameter matching, and maybe look at how pattern matching is used with case statements in other languages; don't try to suggest language designs based on guesses.Excuse me? I know list comprehensions, lambdas and argument unpacking.
And I do not think you can see what I do before I post a message.
About pattern matching in the for statement, I really don't know what they are.
... and? Are you suggesting that if the switch expression is a string and the case expression a compiled regex you could automatically call match instead of testing for equality? If not, how is having regexp even relevant here? And how are recursive functions relevant?I'm suggesting to use if-elif with re module, if you want to use regular expression, and to use recursive functions if you want... recursive functions.
To be more clear, IMHO switch-case is useful if it's simple.
A generator expression is equal to anything except itself, and doesn't contain anything.
You can convert it to an iterable. Probably an overkill, but you can do it.
I don't know what you mean by "symbolic pattern" here.For what I know (not too much), in Mathematica pattern matching can be used for symbols, and symbols can be used as identifiers:
https://reference.wolfram.com/mathematica/guide/Patterns.html
class Case(object):def __init__(self, value):self.value = valuedef __enter__(self):return selfdef __exit__(self, exc_type, exc_value, traceback):passdef __call__(self, *values):return self.value in valuesdef __eq__(self, value):return self.value == valuedef __lt__(self, value):return self.value < valuedef __gt__(self, value):return self.value > value# etc.# can easily add structural matching or regex matching as wellfor i in range(5):print(i, end=' => ')with Case(i) as case:if case(1):print('one')elif case((2,3)):
print('tuple(two, three)')
elif case(2, 3):print('two or three')elif case > 3:print('more than three')
else:
print('unmatched')
0 => unmatched1 => one2 => two or three3 => two or three4 => more than three
On Fri, Apr 18, 2014 at 10:03 AM, Joao S. O. BuenoI haven't been following this thread closely, so I don't know if Lucas
<jsb...@python.org.br> wrote:
> It may be just me, but I fail - in a complete manner - to see how this
> syntax can offer any
> improvement on current if/elif chains.
has mentioned more than syntax, however...
In other languages, the semantics of the switch statement allow the
compiler to generate more efficient code. Instead of testing each
branch of the if statement in succession looking for a match, you
evaluate the switch expression once, then use it as an index into a
jump table of some sort which points directly to the matching case
label. If nothing matches, you jump to the default branch (if one was
defined) or just jump over the entire switch statement (if not).
Skip
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
http://www.voidspace.org.uk/
May you do good and not evil
May you find forgiveness for yourself and forgive others
May you share freely, never taking more than you give.
-- the sqlite blessing http://www.sqlite.org/different.html
In the case of your tarot
example, it is *not* a violation of DRY, because the various elif lines
are not *sources* of knowledge which may contradict each other. The
worst that will happen is that if you change the variable name "tarot"
to something else, your code will fail with a name error.
if your
motive in introducing a switch/case statement is to DRY, I think you
need a better motive.
> @Skip Montanaro: yes, switch statement is used in C also for codeThen I think we're not going to agree on the use or need for switch.
> optimization. Frankly, I think this aspect is unimportant for CPython in
> the present time.
Skip Montanaro writes:
> In other languages, the semantics of the switch statement allow the
> compiler to generate more efficient code. Instead of testing each
> branch of the if statement in succession looking for a match, you
> evaluate the switch expression once, then use it as an index into a
> jump table of some sort which points directly to the matching case
> label.
Sure, but Python already has such a jump table: a function-valued
hash.
Here's a simple class that provides case like syntax. How would modifying the language be better than this?
I think case/elcase would be confusing as it would be unique to python and it's backwards -- the elcase keywords is used for the normal way that people use case statements while the case keyword implements the broken code that inexperienced C programmers frequently write.
switch mystring case "spam":process(thingy)That is not even remotely interpretable as an English sentence. That's not a problem for C, but it is for Python.
No it isn't. An elif clause is embedded in an if statement; there is no if clause you can embed on an if statement, just an entirely new and unrelated if statement.I don't see how skipping over any elcase but falling through to the next case is in any way simpler than C.Well, because it's coherent with if-elif. See my last example in my first message.
bash does not have either ";;&" or ";&"
Who asked you about list comprehensions or lambdas? What do you think they have to do with anything?
[...]
I have no idea why you think recursive functions are relevant to anything being discussed here.
[...]your proposal was to treat tuples specially and match all other iterables as single values, which means that a generator expression would be matched as a single value, meaning it would only match itself. And again, I don't understand what the relevance is supposed to be.
On 19 April 2014 08:52, Bruce Leban bruce-at-leapyear.org |python-ideas-at-python..org| <vajg1...@sneakemail.com> wrote:
Here's a simple class that provides case like syntax. How would modifying the language be better than this?
I don't like the additional unneeded Case indentation. That's why I'm proposing a syntax with switch on the same line with the first case.
for i in range(5):print(i, end=' => ')
case = Case(i)
if case(1):print('one')elif case((2,3)):
print('tuple(two, three)')
elif case(2, 3):print('two or three')elif case > 3:print('more than three')
else:
print('unmatched')
I found that the ugliest part of your proposal since it hides the first case and at the same time makes it appear special when it isn't.
The indentation isn't required with my little Case class. The following works just as well but lacks the visible scoping that the with statement provides (of course the scope of the case variable leaks in either version, but I could modify my class to fail if invoked after __exit__ or __del__)..
for i in range(5):print(i, end=' => ')case = Case(i)if case(1):print('one')elif case((2,3)):print('tuple(two, three)')elif case(2, 3):print('two or three')elif case > 3:print('more than three')else:print('unmatched')
for i in range(5):print(i, end=' => ')
if i == 1:print('one')elif i == (2,3):
print('tuple(two, three)')
elif i in (2, 3):
print('two or three')
elif i > 3:
print('more than three')
else:
print('unmatched')
On 20 April 2014 04:17, Bruce Leban bruce-at-leapyear.org |python-ideas-at-python.org| <vajg1...@sneakemail.com> wrote:
I found that the ugliest part of your proposal since it hides the first case and at the same time makes it appear special when it isn't.Myabe yes, but if you "translate" it in an if-elif, also the if seems to be "special".
Sorry, I didn't read it carefully. Anyway, youe example can be written as:and it's much simpler to read.for i in range(5):print(i, end=' => ')if i == 1:print('one')elif i == (2,3):print('tuple(two, three)')elif i in (2, 3):print('two or three')elif i > 3:print('more than three')else:print('unmatched')
On 19 April 2014 06:54, Andrew Barnert abarnert-at-yahoo.com |python-ideas-at-python.org| <3en9k...@sneakemail.com> wrote:
switch mystring case "spam":process(thingy)That is not even remotely interpretable as an English sentence. That's not a problem for C, but it is for Python.I'll think about a possible solution.No it isn't. An elif clause is embedded in an if statement; there is no if clause you can embed on an if statement, just an entirely new and unrelated if statement.I don't see how skipping over any elcase but falling through to the next case is in any way simpler than C.Well, because it's coherent with if-elif. See my last example in my first message.I can assert, without fear of contradictions, that my proposed syntax is the closest to the if-elif syntax of all the switch statement proposed until now in the age of Python.
About off-topic arguments:bash does not have either ";;&" or ";&"
Who asked you about list comprehensions or lambdas? What do you think they have to do with anything?
[...]
I have no idea why you think recursive functions are relevant to anything being discussed here.
[...]your proposal was to treat tuples specially and match all other iterables as single values, which means that a generator expression would be matched as a single value, meaning it would only match itself. And again, I don't understand what the relevance is supposed to be.I premise that pattern recognition is outside the scope of my switch proposal.
Anyway:
- About list comprehensions and lambdas, you talked about assignment statements, and AFAIK the only pattern matching things that are somewhat related to assignment statement are the possibility to filter a list comprehension and the use of lambdas. If you mean something different you should be more explicit, instead of saying "what? where? why?"
- About recursive function, I wronged. Sorry but it was late.
- About generators, you can create a generator, convert it to an iterable and unpack it in a case_expr.
I could also extend the current syntax and support a "generator unpacking". This way you'll have a limited alternative to pattern matching in some cases in a switch statement without the need to create a static iterable.
On Apr 22, 2014 8:28 AM, "Lucas Malor" <7vsfe...@snkmail.com> wrote:
>
> Sorry, I didn't read it carefully. Anyway, youe example can be written as:
>
>> for i in range(5):
>> print(i, end=' => ')
>> if i == 1:
>> print('one')
>> elif i == (2,3):
>> print('tuple(two, three)')
>> elif i in (2, 3):
>> print('two or three')
>> elif i > 3:
>> print('more than three')
>> else:
>> print('unmatched')
>
> and it's much simpler to read. I suggested the switch statement for a simpler alternative to the if-elif chain.
<sigh> My code can be written as a series of static print statements too and that would be even easier to read. It's an *example* to illustrate use not actual useful code!
My point is that this Case class does what you want with no language changes. And looking at your sample above exactly how would your new switch statement be better than either my code or your if/elif chain?
--- Bruce
(from my phone)
switch tarot case 0:
card = "Fool"
case 1:
card = "Alan Moore"
case 2:
card = "High Priestess"
<etc....>
@case class Nil(): pass @case class Cons(x, xs): pass def reduce(op, my_list): with switch(my_list): if Cons(x, Nil()): return x elif Cons(x, xs): return op(x, reduce(op, xs))
what y’all are missing about switch/case is that it’s not necessarily a series of comparisons in most languages.
in essence, it’s a hashmap lookup with (in some languages) optional comparisons.
in python, the most similar thing is something like this:
def branch_2_3():
spam(1)
spam(6)
{
'key1': lambda: eggs += lay_eggs(), #branch 1
'key2': branch_2_3,
'key3': branch_2_3,
}[value]()
which is of course completely ugly and unnecessarily complicated for cases where multiple keys lead to the same branch.
1) Operator precedence gets in the way. (Easily fixed.)
>>> lambda: eggs += lay_eggs()
SyntaxError: can't assign to lambda
2) Assignment is a statement, so you can't do it in a lambda.
>>> lambda: (eggs += lay_eggs())
SyntaxError: invalid syntax
3) Assignment makes it local, so you'll get UnboundLocalError if you
don't declare it nonlocal.
>>> def f():
eggs += lay_eggs()
>>> f()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
f()
File "<pyshell#4>", line 2, in f
eggs += lay_eggs()
UnboundLocalError: local variable 'eggs' referenced before assignment
Hence my previous statement that you need to write the function out of
line, which breaks the switch-ness of the block, and you definitely
need to declare eggs nonlocal. Now, if eggs is a list, you can extend
it with a method (which can be called in an expression), but if your
switch block can only do expressions, it's pretty limited.
interesting. it’s still assignment, even if nothing gets assigned (and only __iadd__
gets called behind the scenes).
Another difference is a typical switch hash map is usually built at compile
time in other languages. In python, the example's dict would be built
every time it executes. That can be an advantage at times, but it can also
make it slower than an if-elif's Moving the dict (and function
definitions) to a place where it's only initialised once, may mean the
names it access may not be reachable, so in most cases any values it
depends on need to be passed as well.
Ron
> what y’all are missing about switch/case is that it’s *not*
> necessarily a series of comparisons in most languages.
I don't think anybody missed this. It's certainly been mentioned
several times.
Of course looking at a specific, full-featured example implementing
the idea of "switch on a value" using (and abusing, as pointed out by
Chris) existing Python features leads to the conclusion that such an
implementation
> is of course completely ugly and unnecessarily complicated for
> cases where multiple keys lead to the same branch.
I can't imagine anybody has missed this. It is presumably one reason
why the OP is requesting new syntax. This also has a more fundamental
flaw than "ugly" in many eyes: the need to define behavior *before*
defining the case.
The reason why people keep coming back to a series of comparisons
(actually more general: a series of branch on tests) is that a series
of comparisons is behaviorally equivalent to a switch. Written in
appropriate style it's obvious that it is intended to have the effect
of a switch. The code implementing a case is given "naturally" after
mentioning the case. Given all that, it seems unnecessary to add new
syntax.
However, it's been mentioned that *if* we're going to have a case
statement, it would be nice to give it "pattern matching" abilities
such as the globbing of sh's 'case' or Haskell's signature matching.
That power might be enough to get some people to swing over to
supporting a syntax addition, it seems.
Starting from that summary of the thread, what are you trying to
argue?
*Some* people might be missing that, but not *all* of us :-)
Before commenting further, anyone who isn't familiar with the two
rejected PEPs on this topic should read them:
http://legacy.python.org/dev/peps/pep-0275/
http://legacy.python.org/dev/peps/pep-3103/
Also, folks might like to review the wide range of semantics and syntax
for switch/case in different languages:
http://rosettacode.org/wiki/Conditional_Structures
By my count, there are:
50 languages with no case/switch at all
30 languages using the keyword "case"
19 using "switch"
15 using "select"
3 using "when"
2 using "match"
2 using "given" (treating Perl 5 and 6 as distinct languages)
1 using "evaluate"
1 using "caseof"
1 using "if" (with a switch-like syntax)
The above counts should be mostly accurate, but I don't guarantee that
they are exactly correct. Apart from the possibility of counting errors,
there were a few cases where I had to make a judgement call on whether
or not language feature X was analogous to a switch or not.
--
Steven
The reason why people keep coming back to a series of comparisons
(actually more general: a series of branch on tests) is that a series
of comparisons is behaviorally equivalent to a switch. Written in
appropriate style it's obvious that it is intended to have the effect
of a switch. The code implementing a case is given "naturally" after
mentioning the case. Given all that, it seems unnecessary to add new
syntax.
Starting from that summary of the thread, what are you trying to
argue?
Also, folks might like to review the wide range of semantics and syntax
for switch/case in different languages:
http://rosettacode.org/wiki/Conditional_Structures
Augmented assignment statements are specialized assignment statements.
They are documented in a subsection of the assignment statement section.
https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements
Something is always rebound, even if it is the same object. The
interpreter does not know whether __iadd__ will return the same object
or a new object -- and it does not check after.
Not understanding that augmented assignment always assigns trips up
beginners who mistakenly and unnecessarily try to use it to mutate a
member of a tuple.
>>> t = ([],)
>>> t[0] += [1,2]
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
t[0] += [1,2]
TypeError: 'tuple' object does not support item assignment
>>> t
([1, 2],)
This is equivalent to* and effectively executed as
>>> t = ([],)
>>> t[0] = t[0].__iadd__ ([1,2])
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
t[0] = t[0].__iadd__ ([1,2])
TypeError: 'tuple' object does not support item assignment
>>> t
([1, 2],)
* The difference is that 't' is only evaluated once and the resulting
reference is used for both subscriptions.
The proper way to mutate a tuple member is to directly mutate it.
>>> t = ([],)
>>> t[0].extend([1,2])
>>> t
([1, 2],)
--
Terry Jan Reedy