Why is print(x += 1) invalid syntax? - python

This works just fine
x = 0
while True:
x += 1
print(x)
while this
x = 0
while True:
print(x += 1)
doesn't
I want a program that counts to infinity or at least until max digits

Unlike many other languages, where an assignment is an expression and evaluates to the assigned value, in Python an assignment is its own statement. Therefore it can't be used in an expression.
One advantage of this is that if you forget an = in an if statement (i.e. you meant to write == but wrote = instead) you get an error:
if a = b: # this is an assignment not a comparison! SyntaxError
In certain other languages this is valid syntactically but wouldn't give you the result you intend, causing hair-loss bugs. (This is one reason linters were invented. The language itself didn't prevent you from making this mistake, so they created an external tool to help with that.)
Python 3.8 adds the assignment operator, :=, a.k.a. the walrus operator. It behaves like assignment in other languages, although you still can't use it everywhere. So this works:
x = 0
while True:
print(x := x + 1)
Unfortunately (or fortunately) there is no +:=, which I guess you'd call an augmented walrus.

Because the argument to print() needs to be an expression, and an assignment statement is not an expression.
The walrus operator := was introduced in Python precisely to allow you to do this, though it does not have a variant which allows you to increment something. But you can say
x = 0
while True:
print(x := x + 1)
This does not strike me as a particularly good or idiomatic use of this operator, though.

Related

Why break cannot be used in ternary operator in python? [duplicate]

I am trying to use if-else expression which is supposed to break the loop if the if condition fails, but getting an invalid syntax error.
Sample code:
a = 5
while True:
print(a) if a > 0 else break
a-=1
Of course, if I write in the traditional way (not using the one liner) it works.
What is wrong in using the break command after the else keyword?
If I run this, I get the following error:
... print(a) if a > 0 else break
File "<stdin>", line 2
print(a) if a > 0 else break
^
SyntaxError: invalid syntax
This is because
print(a) if a > 5 else break
is a ternary operator. Ternary operators are no if statements. These work with syntax:
<expr1> if <expr2> else <expr3>
It is equivalent to a "virtual function":
def f():
if <expr2>:
return <expr1>
else:
return <expr3>
So that means the part next to the else should be an expression. break is not an expression, it is a statement. So Python does not expect that. You can not return a break.
In python-2.x, print was not a function either. So this would error with the print statement. In python-2.x print was a keyword.
You can rewrite your code to:
a = 5
while True:
if a > 5:
print(a)
else:
break
a -= 1
You can read more about this in the documentation and PEP-308.
If is an expression, break similar to return is a statement. You can't use two statements in a single sentence (unless you use a semicolon which is ugly). I know it would have been really cool if we can do that, but alas that's the way it is.
To put it in slightly simpler terms, you're misusing the 'one-line if statement' (ternary operator). It always evaluates to an expression (i.e., a value). That is,
<expr1> if <condition> else <expr2>
evaluates to <expr1> if <condition> is True, and to <expr2> if <condition> is False. This resulting value can then be used like any Python value, for example:
y = 0
x = (5 if (y > 0) else 6)
print(x) # 6
Of course, the parentheses are completely unnecessary (even discouraged), but hopefully are useful for understanding the meaning of that line.
Therefore,
print(a) if a > 0 else break
tries to evaluate print(a) (which, by the definition of print() in Python 3, always returns None – perfectly valid, but probably not what you usually want) and then break, which does not evaluate to anything because it is a statement (action), not an expression (value), hence the invalid syntax error.
Hence, if you want to execute one of two statements depending on a condition, you really need the multi-line solution proposed by
Willem Van Onsem. There may be hacky ways to do it in one line, but multiple lines is the usual solution for something like this in Python.

How can an assignment statement "x = y := f(x)" be done when using assignment expressions in Python?

I read in Twitter:
#Python news: Guido accepted PEP 572. Python now has assignment expressions.
if (match := (pattern.search) pattern.search(data)) is not None:
print((match.group) mo.group(1))
filtered_data = [y for x in data if (y := f(x)) is not None]
(correction mine in the 2nd line of code)
As indicated, PEP 572 -- Assignment Expressions describes this to be present in Python 3.8:
This is a proposal for creating a way to assign to variables within an expression using the notation NAME := expr.
I've gone through the description and examples and I see this is a convenient way to avoid repetitions of either calls or assignments, so instead of:
match1 = pattern1.match(data)
match2 = pattern2.match(data)
if match1:
return match1.group(1)
elif match2:
return match2.group(2)
or the more efficient:
match1 = pattern1.match(data)
if match1:
return match1.group(1)
else:
match2 = pattern2.match(data)
if match2:
return match2.group(2)
One now can say:
if match1 := pattern1.match(data):
return match1.group(1)
elif match2 := pattern2.match(data):
return match2.group(2)
Similarly, one can now say:
if any(len(longline := line) >= 100 for line in lines):
print("Extremely long line:", longline)
However, I do not understand how this example given in the PEP is not valid:
y0 = y1 := f(x) # INVALID
Will it be correct to say y0 = (y1 := f(x))? How could it be used?
Foot note for those who wonder where will this be available: I already installed Python 3.7 and it does not work there, since the PEP currently shows as "Status: Draft". However, the PEP talks about Proof of concept / reference implementation (https://github.com/Rosuav/cpython/tree/assignment-expressions), so it is a matter of using their Python 3.8 alpha 0 version that includes it.
As explicitly stated in the PEP,
Unparenthesized assignment expressions are prohibited at the top level in the right hand side of an assignment statement; for example, the following is not allowed:
y0 = y1 := f(x) # INVALID
Again, this rule is included to avoid two visually similar ways of saying the same thing.
And later,
As follows from section "Exceptional cases" above, it is never allowed at the same level as =. In case a different grouping is desired, parentheses should be used.
...
# INVALID
x = y := 0
# Valid alternative
x = (y := 0)

":=" syntax and assignment expressions: what and why?

PEP 572 introduces assignment expressions (colloquially known as the Walrus Operator), implemented for Python 3.8. This seems like a really substantial new feature since it will allow this form of assignment within comprehensions and lambda functions.
What exactly are the syntax, semantics, and grammar specification of assignment expressions?
Why is this new (and seemingly quite radical concept) being introduced, when a similar idea in PEP 379 on "Adding an assignment expression" was rejected before?
PEP 572 contains many of the details, especially for the first question. I'll try to summarise/quote concisely arguably some of the most important parts of the PEP:
Rationale
Allowing this form of assignment within comprehensions, such as list comprehensions, and lambda functions where traditional assignments are forbidden. This can also facilitate interactive debugging without the need for code refactoring.
Recommended use-case examples
a) Getting conditional values
for example (in Python 3):
command = input("> ")
while command != "quit":
print("You entered:", command)
command = input("> ")
can become:
while (command := input("> ")) != "quit":
print("You entered:", command)
Similarly, from the docs:
In this example, the assignment expression helps avoid calling len()
twice:
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
b) Simplifying list comprehensions
for example:
stuff = [(lambda y: [y,x/y])(f(x)) for x in range(5)]
can become:
stuff = [[y := f(x), x/y] for x in range(5)]
Syntax and semantics
In any context where arbitrary Python expressions can be used, a named expression can appear. This is of the form name := expr where expr is any valid Python expression, and name is an identifier.
The value of such a named expression is the same as the incorporated expression, with the additional side-effect that the target is assigned that value
Differences from regular assignment statements
In addition to being an expression rather than statement, there are several differences mentioned in the PEP: expression assignments go right-to-left, have different priority around commas, and do not support:
Multiple targets
x = y = z = 0 # Equivalent: (z := (y := (x := 0)))
Assignments not to a single name:
# No equivalent
a[i] = x
self.rest = []
Iterable packing/unpacking
# Equivalent needs extra parentheses
loc = x, y # Use (loc := (x, y))
info = name, phone, *rest # Use (info := (name, phone, *rest))
# No equivalent
px, py, pz = position
name, phone, email, *other_info = contact
Inline type annotations:
# Closest equivalent is "p: Optional[int]" as a separate declaration
p: Optional[int] = None
Augmented assignment is not supported:
total += tax # Equivalent: (total := total + tax)
A couple of my favorite examples of where assignment expressions can make code more concise and easier to read:
if statement
Before:
match = pattern.match(line)
if match:
return match.group(1)
After:
if match := pattern.match(line):
return match.group(1)
Infinite while statement
Before:
while True:
data = f.read(1024)
if not data:
break
use(data)
After:
while data := f.read(1024):
use(data)
There are other good examples in the PEP.
A few more examples and rationales now that 3.8 has been officially released.
Naming the result of an expression is an important part of programming, allowing a descriptive name to be used in place of a longer expression, and permitting reuse. Currently, this feature is available only in statement form, making it unavailable in list comprehensions and other expression contexts.
Source: LicensedProfessional's reddit comment
Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
process(chunk)
Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]
Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
What is := operator?
In simple terms := is a expression + assignment operator.
it executes an expression and assigns the result of that expression in a single variable.
Why is := operator needed?
simple useful case will be to reduce function calls in comprehensions while maintaining the redability.
lets consider a list comprehension to add one and filter if result is grater than 0 without a := operator. Here we need to call the add_one function twice.
[add_one(num) for num in numbers if add_one(num) > 0]
Case 1:
def add_one(num):
return num + 1
numbers = [1,2,3,4,-2,45,6]
result1 = [value for num in numbers if (value := add_one(num)) > 0]
>>> result1
[2, 3, 4, 5, 46, 7]
The result is as expected and we don't need to call the add_one function to call twice which shows the advantage of := operator
be cautious with walarus := operator while using list comprehension
below cases might help you better understand the use of := operator
Case 2:
def add_one(num):
return num + 1
numbers = [1,2,3,4,-2,45,6]
>>> result2 = [(value := add_one(num)) for num in numbers if value > 0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
NameError: name 'value' is not defined
Case 3: when a global variable is set to positive
def add_one(num):
return num + 1
numbers = [1,2,3,4,-2,45,6]
value = 1
result3 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result3
[2, 3, 4, 5, -1]
Case 4: when a global variable is set to negitive
def add_one(num):
return num + 1
numbers = [1,2,3,4,-2,45,6]
value = -1
result4 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result4
[]

Assign two variables with ternary operator

Is there a way to do variable assignments(as shown below) using ternary operators in python:
if(x>1):
y="yes"
else:
z="yes"
Something like (x='yes') if(x>1) else (z='yes'), but this gives an error. Is there any other way to do this?
I know single variable assignments can be done like this: x="yes" if(l==0) else "no"
Edit: Assume x, y & z are assigned with some value before this is run.
You can use the following hack, which employs tuple unpacking:
y, z = ('yes', z) if x > 1 else (y, 'yes')
Don't miss those parentheses.
I wouldn't really recommend using this as it is harder to understand, has one redundant assignment statements, and uses unpacking unnecessarily. I'd recommend going for the normal if statement wherever you can.
This is what it would be with normal ifs:
if x > 1:
y = 'yes'
z = z
else:
y = y
z = 'yes'
No, you can't do that. You can only have expressions, not statements, inside the ternary. print works because (in Python 3 at least) it is a function call, therefore an expression; but assignment is always a statement.
varname = 'y' if x > 1 else 'z'
If you want to assign a global variable:
globals()[varname] = 'yes'
If you want to set an attribute on an object:
setattr(obj, varname, 'yes')
If it's a local variable inside a function, you have to get more hacky:
exec('%s = "yes"' % varname)
or
exec(varname + ' = "yes"')
You can of course place the definition of varname directly in all of these statements to keep it in one line, I'm just avoiding repetition.
But really it's best to not do any of these. Keep it simple and straightforward.
You can use exec function like this:
exec("y='yes'" if x > 1 else "z='yes'")

python (bool) ? then : else syntax? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python Ternary Operator
In some languages including Java, C/C++, C#, etc. you can assign a value based on the result of an inline boolean expression.
For example,
return (i < x) ? i : x
This will return i if i < x, otherwise it will return x. I like this because it is much more compact in many cases than the longer syntax which follows.
if (i < x)
return i
else
return x
Is it possible to use this syntax in python and if so, how?
You can use (x if cond else y), e.g.
>>> x = 0
>>> y = 1
>>> print("a" if x < y else "b")
a
That will work will lambda function too.
Yes, it looks like this:
return i if i < x else x
It's called the conditional operator in python.
a if b else c syntax was introduced in Python 2.5. Most people have already upgraded to the recent version but in legacy code you may find another approach:
some_var = a<b and a or c
If you ever will be using this syntax remember that a must not evaluate to False.
As the other answers state, Python's version for this is:
i if i < x else x
(of course for this particular example, one would prefer writing
min(i, x) , as it is easier on the eyes)
However, this expression syntax was just made available on Python 2.5 (I think it was around 2004). before that, the following idiom was used - but care should be taken, as it is error prone:
i < x and i or x - because the logical operator "and" actually evaluates to the last true value on its chain - therefore, if the expression was true, i < x and i evaluates to i - and the or operator evaluates to first true value on the chain. On this case, if i < x would be false, so would i< x and i and the first true value would be x.
It is easy to see how error prone this construct was, since if the boolean value of i would be false ( for example if i==0), than it would return x, even if i < x where true.
I myself, back in those days, used to write this construct instead:
(x, i)[i < x]
The expression "i < x" ha a numerical value of "1" (true) or "0" (false) and I used this proeprty to have it work as an index to the previous tuple. The problem with this approach was that it would always evaluate both expressions on the tuple, even though it would use ony one of then (if the expressions where function calls this could get expensive, or even conflicting)
Ternary operator in python.
a if b else c
>>> a=1
>>> b=2
>>> a if a<b else b
1
>>> a if a>b else b
2
Try this in Python:
return i if i < x else x
It's exactly the equivalent of the following expression in Java, C, C++ C#
return i < x ? i : x;
Read more about Python's conditional expressions.

Categories