7

Consider the following code snippet. It flags a syntax error at the break statement.

digits = list(str(102))
dummy = list(str(102/2))
for j in digits:
    dummy.remove(j) if j in dummy else break

How do I fix this?(I want to still use the ternary operator)

SparkAndShine
  • 17,001
  • 22
  • 90
  • 134
TheChetan
  • 4,440
  • 3
  • 32
  • 41

2 Answers2

6

Edit:

(see my conversation with Stefan Pochmann in the comments)

Ternary operator is not for only statement, but rather for assignment or for expression (and break is an only statement):

a = 5 if cond else 3 #OK
do() if cond else dont() #also OK
do() if cond else break #not OK

use normal if-else statement to do statements:

if cond:
    do()
else:
    break
Community
  • 1
  • 1
Ian
  • 30,182
  • 19
  • 69
  • 107
  • This is fine, but when I have to type it 5-6 times it looks ugly, is there a workaround? – TheChetan May 13 '16 at 15:55
  • 2
    Your examples aren't quite the same, as they *are* valid syntax – jonrsharpe May 13 '16 at 15:55
  • 3
    @TheChetan: no, there isn't. Python focuses on readability, not conciseness. – Martijn Pieters May 13 '16 at 15:56
  • 1
    @TheChetan hmm... depends on the case, you could in fact make for loop and store the functions in `list` first for instance – Ian May 13 '16 at 15:56
  • @Ian, why does `dummy.remove(j) if j in dummy else exit` works? – SparkAndShine May 13 '16 at 16:06
  • I wouldn't hide the `break` argument in parentheses, as that's the main point. The ternary *can* be used for statements, e.g., `1 if 2 else 3` works fine as statement. – Stefan Pochmann May 13 '16 at 16:08
  • @sparkandshine you mean your answer? why it does not have syntax error? Hmm... I can help you check if you want, give me a minute – Ian May 13 '16 at 16:08
  • @StefanPochmann you are actually right, I think my terminology is inaccurate. But continue or break definitely not working... are they not statement? – Ian May 13 '16 at 16:09
  • @Ian `continue` and `break` are statements. Not sure why you're asking that, though. – Stefan Pochmann May 13 '16 at 16:11
  • 1
    @sparkandshine ah I get it... that is because `exit` is a function. If you use tool like `PyCharm` you can see that.. it is like function returning nothing – Ian May 13 '16 at 16:11
  • @StefanPochmann I mean, if they are statements and they (`break` and `continue`) cannot be used in the ternary, but other statement can (like your example), it means to say that statement cannot be put in the ternary is inaccurate. I was asking if my terminology in my answer is inaccurate – Ian May 13 '16 at 16:14
  • 1
    @Ian Ah, ok. The problem is that they're *only* statements, not expressions, and you [need an expression](https://docs.python.org/3/reference/expressions.html#conditional-expressions) there. – Stefan Pochmann May 13 '16 at 16:18
  • @StefanPochmann ah I get it, the correct phrase should be "not for *only* statement, it is for assignment and *expression*" :) – Ian May 13 '16 at 16:20
  • @Ian No, when you say it's "for assignment", that sounds like you're thinking of it as `(a = 5) if cond else 3` rather than `a = (5 if cond else 3)`. Doesn't really have anything to do with assignments. The issue simply is that the ternary needs an expression there and that `break` is no expression. – Stefan Pochmann May 13 '16 at 16:31
  • @StefanPochmann ah, no. I didn't mean that. The `a=5` is an expression *with* assignment I would say. But yes, the keyword here is *expression* - I mis-term it as *condition* initially. Thanks for the comment. – Ian May 13 '16 at 16:33
  • @Ian, I post my confusion as a question, ['exit' is not a keyword in Python, but no error occurs while using it](http://stackoverflow.com/questions/37215406/exit-is-not-a-keyword-in-python-but-no-error-occurs-while-using-it), waiting for your answer:-) – SparkAndShine May 13 '16 at 16:51
3

You cannot use break in Your loop logic can be re written using itertools.takewhile if you want a more succinct solution

digits = list(str(102))
dummy = list(str(102/2))

from itertools import takewhile

for d in takewhile(dummy.__contains__, digits):
    dummy.remove(d)

You can also remove the need for the else using a for loop by reversing your logic, check if j is not in dummy breaking when that is True:

for j in digits:
    if j not in dummy:
        break
    dummy.remove(j)

Also if you want to remove all occurrences of any of the initial elements from digits that are in dummy, remove won't do that for any repeating elements but using a list comp after creating a set of elements to remove will:

digits = str(102)
dummy = list(str(102/2))
st = set(takewhile(dummy.__contains__, digits))
dummy[:] = [d for d in dummy if d not in st]

print(dummy)

You can also iterate over a string so no need to call list on digits unless you plan on doing some list operations with it after.

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • 2
    Ugh, it's awkward to avoid the dunder method here; you can use `functools.partial` and `operator.contains`, e.g. `in_dummy = partial(contains, dummy)`. This seems like a lot of work to avoid the underscore method though, and no real benefits. A lambda could be used alternatively, e.g. `lambda e: e in dummy`. – Jared Goguen May 13 '16 at 16:42
  • @JaredGoguen, yep, I don't mind using `__contains__` in a case like this, definitely better than a lambda, might be interesting to time the comparisons. – Padraic Cunningham May 13 '16 at 16:49