Can I use += on multiple variables on one line? - python

While shortening my code I was cutting down a few variable declarations onto one line-
##For example- going from-
Var1 =15
Var2 = 26
Var3 = 922
##To-
Var1, Var2, Var3 = 15, 26, 922
However, when I tried doing the same thing to this code-
User_Input += Master_Key[Input_ref]
Key += Master_Key[Key_ref]
Key2 += Master_Key[Key_2_Ref]
##Which looks like-
User_Input, Key, Key2 += Master_Key[Input_Ref], Master_Key[Key_Ref], Master_Key[Key_2_Ref]
This throws the error
SyntaxError: illegal expression for augmented assignment
I have read the relevant Python documentation, but I still can't find a way to shorten this particular bit of code.

No, you cannot. You cannot use augmented assignment together with multiple targets.
You can see this in the Augmented assignment statements section you linked to:
augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)
augtarget ::= identifier | attributeref | subscription | slicing
The augtarget rule only allows for one target. Compare this with the Assignment statements rules:
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)
target_list ::= target ("," target)* [","]
target ::= identifier
| "(" target_list ")"
| "[" target_list "]"
| attributeref
| subscription
| slicing
where you have a target_list rule to assign to.
I'd not try and shorten this at all; trying to squeeze augmented assignments onto one line does not improve readability or comprehension of what is happening.

Related

different syntax for accessing method of literals [duplicate]

This question already has answers here:
Accessing attributes on literals work on all types, but not `int`; why? [duplicate]
(4 answers)
Closed 1 year ago.
Why we can directly access a method belonging to a string literal:
keyfunc="STR".__eq__
or a float constant:
keyfunc=1.0.__eq__
or even:
keyfunc=1..__eq__ # 1. is the same float as 1.0
but the same code for an integer throws a syntax error?
keyfunc=1.__eq__ # WRONG!
The last line should be written as:
keyfunc=(1).__eq__
Why and when are the parens required?
Reason for this to happen:
It's because 1. gets treated as a float:
>>> 1.
1.0
>>>
And obviously:
>>> 1.0__eq__
SyntaxError: invalid decimal literal
>>>
Would give an error.
It's because Python operates from left to right. So the dot would belong to 1 to make it a float.
Workaround for this other than parenthesis:
So the way to fix it would be to add a space between 1 and the dot ., like:
>>> 1 .__eq__
<method-wrapper '__eq__' of int object at 0x00007FFDF14B2730>
>>>
Reasoning for these workarounds to work:
The reason this works is because:
>>> 1 .
SyntaxError: invalid syntax
>>>
Gives an error, so it doesn't get treated as an integer.
It's the same case for (1).
As you can see:
>>> (1).
SyntaxError: invalid syntax
>>>
Documentation references:
As shown on this page of the documentation:
integer ::= decimalinteger | octinteger | hexinteger | bininteger
decimalinteger ::= nonzerodigit digit* | "0"+
nonzerodigit ::= "1"..."9"
digit ::= "0"..."9"
octinteger ::= "0" ("o" | "O") octdigit+
hexinteger ::= "0" ("x" | "X") hexdigit+
bininteger ::= "0" ("b" | "B") bindigit+
octdigit ::= "0"..."7"
hexdigit ::= digit | "a"..."f" | "A"..."F"
bindigit ::= "0" | "1"
The above is the integer literal definitions in Python.
It also has to do with the python compiler specification. But the main confusion is that we don't know who the period belongs to, i.e.
keyfunc=[1.]__eq__ or keyfunc=1[.__eq__]. Without specifying the (1) or (as mentioned) adding the space, we don't know whether it's the end of an expression or not.

python grammar: wrong atom definition?

While checking python grammar at official documentation, here is what it reads
atom_expr: ['await'] atom trailer*
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
Then, 10.bit_length() is a valid syntax according to that definition but not according to the python interpreter. Instead, n=10;n.bit_length() is valid syntax for both the specifications and the interpreter.
Where should I find the real definition of atom and atom_expr?
Thanks to juanpa's comment and the answers in the related question, it appears that the problem comes from 10.. The definition of NUMBER includes the dot such that 10.bit_length() is of kind NUMBER NAME trailer and not NUMBER '.' NAME trailer.
In order to obtain an atom_expr, one must separate the dot: both 10 .bit_length() and (10).bit_length() give the correct answer.

How do you use multiple arguments in {} when using the .format() method in Python

I want a table in python to print like this:
Clearly, I want to use the .format() method, but I have long floats that look like this: 1464.1000000000001 I need the floats to be rounded, so that they look like this: 1464.10 (always two decimal places, even if both are zeros, so I can't use the round() function).
I can round the floats using "{0:.2f}".format("1464.1000000000001"), but then they do not print into nice tables.
I can put them into nice tables by doing "{0:>15}.format("1464.1000000000001"), but then they are not rounded.
Is there a way to do both? Something like "{0:>15,.2f}.format("1464.1000000000001")?
You were almost there, just remove the comma (and pass in a float number, not a string):
"{0:>15.2f}".format(1464.1000000000001)
See the Format Specification Mini-Language section:
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= integer
precision ::= integer
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
Breaking the above format down then:
fill: <empty>
align: < # left
sign: <not specified>
width: 15
precision: 2
type: `f`
Demo:
>>> "{0:>15.2f}".format(1464.1000000000001)
' 1464.10'
Note that for numbers, the default alignment is to the right, so the > could be omitted.
"{0:15.2f}".format(1464.1000000000001)
I always find this site useful for this stuff:
https://pyformat.info/

Python tuple assignment and checking in conditional statements [duplicate]

This question already has answers here:
When are parentheses required around a tuple?
(3 answers)
Closed 8 years ago.
So I stumbled into a particular behaviour of tuples in python that I was wondering if there is a particular reason for it happening.
While we are perfectly capable of assigning a tuple to a variable without
explicitely enclosing it in parentheses:
>>> foo_bar_tuple = "foo","bar"
>>>
we are not able to print or check in a conditional if statement the variable containing
the tuple in the previous fashion (without explicitely typing the parentheses):
>>> print foo_bar_tuple == "foo","bar"
False bar
>>> if foo_bar_tuple == "foo","bar": pass
SyntaxError: invalid syntax
>>>
>>> print foo_bar_tuple == ("foo","bar")
True
>>>
>>> if foo_bar_tuple == ("foo","bar"): pass
>>>
Does anyone why?
Thanks in advance and although I didn't find any similar topic please inform me if you think it is a possible dublicate.
Cheers,
Alex
It's because the expressions separated by commas are evaluated before the whole comma-separated tuple (which is an "expression list" in the terminology of the Python grammar). So when you do foo_bar_tuple=="foo", "bar", that is interpreted as (foo_bar_tuple=="foo"), "bar". This behavior is described in the documentation.
You can see this if you just write such an expression by itself:
>>> 1, 2 == 1, 2 # interpreted as "1, (2==1), 2"
(1, False, 2)
The SyntaxError for the unparenthesized tuple is because an unparenthesized tuple is not an "atom" in the Python grammar, which means it's not valid as the sole content of an if condition. (You can verify this for yourself by tracing around the grammar.)
Considering an example of if 1 == 1,2: which should cause SyntaxError, following the full grammar:
if 1 == 1,2:
Using the if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite], we get to shift the if keyword and start parsing 1 == 1,2:
For the test rule, only first production matches:
test: or_test ['if' or_test 'else' test] | lambdef
Then we get:
or_test: and_test ('or' and_test)*
And step down into and_test:
and_test: not_test ('and' not_test)*
Here we just step into not_test at the moment:
not_test: 'not' not_test | comparison
Notice, our input is 1 == 1,2:, thus the first production doesn't match and we check the other one: (1)
comparison: expr (comp_op expr)*
Continuing on stepping down (we take the only the first non-terminal as the zero-or-more star requires a terminal we don't have at all in our input):
expr: xor_expr ('|' xor_expr)*
xor_expr: and_expr ('^' and_expr)*
and_expr: shift_expr ('&' shift_expr)*
shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power
Now we use the power production:
power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
And shift NUMBER (1 in our input) and reduce. Now we are back at (1) with input ==1,2: to parse. == matches comp_op:
comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
So we shift it and reduce, leaving us with input 1,2: (current parsing output is NUMBER comp_op, we need to match expr now). We repeat the process for the left-hand side, going straight to the atom nonterminal and selecting the NUMBER production. Shift and reduce.
Since , does not match any comp_op we reduce the test non-terminal and receive 'if' NUMBER comp_op NUMBER. We need to match else, elif or : now, but we have , so we fail with SyntaxError.
I think the operator precedence table summarizes this nicely:
You'll see that comparisons come before expressions, which are actually dead last.
in, not in, is, is not, Comparisons, including membership tests
<, <=, >, >=, <>, !=, == and identity tests
...
(expressions...), [expressions...], Binding or tuple display, list display,
{key: value...}, `expressions...` dictionary display, string conversion

why do nested control statements on one line fail in python?

I have been trying to convert a simple execution such as:
for x in xrange(10):
if x % 2 == 0:
print x, 'is even'
to a one liner version:
for x in xrange(10): if x % 2 == 0: print x, 'is even'
which gives me:
File "foo.py", line 1
for x in xrange(10): if x % 2 == 0: print x, 'is even'
^
SyntaxError: invalid syntax
I don't see any ambiguity in here. Is there a particular reason why this fails?
It's simply not allowed by the grammar. The relevant productions are:
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
As you can see, after a for you can either put a simple statement or a "suite", i.e. an indented block. An if statement is a compound statement, not a simple one.
Two lines are the minimum to express this program:
for x in xrange(10):
if x % 2 == 0: print x, 'is even'
(Of course, you can write equivalent programs that take only one line, such as
for x in xrange(0, 10, 2): print x, "is even"
or any of the other one-liners posted in response to this question.)
From the formal grammar for 2.7:
compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt)
If the suite had allowed a compound_stmt then what you suggest would be accepted. But that would also allow something like this:
if True: try:
# do something
except:
# handle
foo()
Is that except outside the enclosing if? Is the call to foo outside the enclosing if? I think this shows that we really don't want in-lining compound statements to be allowed by the formal grammar. Simply adding suite: compound_stmt makes the grammar ambiguous as I read it, where the same code can be interpreted with two or more different meanings, neither disprovable.
Basically, it's by design that what you ask is a parse error. Reworking the formal grammar could allow the code in your example to work without other funny stuff, but it requires careful attention to ambiguity and other problems.
See also Dangling Else, a grammar problem that afflicted the standard Algol-60 language. It's not always easy to find these kinds of problems, so a healthy fear of changing a working grammar is a good thing.
try something like :
In [14]: from __future__ import print_function
In [17]: for x in xrange(10): print (x,'is even') if x%2==0 else None
....:
0 is even
2 is even
4 is even
6 is even
8 is even
If you want something similar, use a list comprehension:
print '\n'.join('{0} is even'.format(x) for x in xrange(10) if x % 2 == 0)
Prints:
0 is even
2 is even
4 is even
6 is even
8 is even
You can try this:
for y in (x for x in xrange(10) if x % 2 == 0): print y
The Python docs about compound statements states a reason for the decision on why does the grammar disallow this:
A suite can be one or more semicolon-separated simple statements on
the same line as the header, following the header’s colon, or it can
be one or more indented statements on subsequent lines. Only the
latter form of suite can contain nested compound statements; the
following is illegal, mostly because it wouldn’t be clear to which if
clause a following else clause would belong:
if test1: if test2: print x
So in wberry's answer it was right about the Dangling Else.

Categories