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.
Related
Is there an easy way with Python f-strings to fix the number of digits after the decimal point? (Specifically f-strings, not other string formatting options like .format or %)
For example, let's say I want to display 2 digits after the decimal place.
How do I do that? Let's say that
a = 10.1234
Include the type specifier in your format expression:
>>> a = 10.1234
>>> f'{a:.2f}'
'10.12'
When it comes to float numbers, you can use format specifiers:
f'{value:{width}.{precision}}'
where:
value is any expression that evaluates to a number
width specifies the number of characters used in total to display, but if value needs more space than the width specifies then the additional space is used.
precision indicates the number of characters used after the decimal point
What you are missing is the type specifier for your decimal value. In this link, you an find the available presentation types for floating point and decimal.
Here you have some examples, using the f (Fixed point) presentation type:
# notice that it adds spaces to reach the number of characters specified by width
In [1]: f'{1 + 3 * 1.5:10.3f}'
Out[1]: ' 5.500'
# notice that it uses more characters than the ones specified in width
In [2]: f'{3000 + 3 ** (1 / 2):2.1f}'
Out[2]: '3001.7'
In [3]: f'{1.2345 + 4 ** (1 / 2):9.6f}'
Out[3]: ' 3.234500'
# omitting width but providing precision will use the required characters to display the number with the the specified decimal places
In [4]: f'{1.2345 + 3 * 2:.3f}'
Out[4]: '7.234'
# not specifying the format will display the number with as many digits as Python calculates
In [5]: f'{1.2345 + 3 * 0.5}'
Out[5]: '2.7344999999999997'
Adding to Robᵩ's answer: in case you want to print rather large numbers, using thousand separators can be a great help (note the comma).
>>> f'{a*1000:,.2f}'
'10,123.40'
And in case you want to pad/use a fixed width, the width goes before the comma:
>>> f'{a*1000:20,.2f}'
' 10,123.40'
Adding to Rob's answer, you can use format specifiers with f strings (more here).
You can control the number of decimals:
pi = 3.141592653589793238462643383279
print(f'The first 6 decimals of pi are {pi:.6f}.')
The first 6 decimals of pi are 3.141593.
You can convert to percentage:
grade = 29/45
print(f'My grade rounded to 3 decimals is {grade:.3%}.')
My grade rounded to 3 decimals is 64.444%.
You can do other things like print constant length:
from random import randint
for i in range(5):
print(f'My money is {randint(0, 150):>3}$')
My money is 126$
My money is 7$
My money is 136$
My money is 15$
My money is 88$
Or even print with a comma thousand separator:
print(f'I am worth {10000000000:,}$')
I am worth 10,000,000,000$
Consider:
>>> number1 = 10.1234
>>> f'{number1:.2f}'
'10.12'
Syntax:
"{" [field_name] ["!" conversion] [":" format_spec] "}"
Explanation:
# Let's break it down...
# [field_name] => number1
# ["!" conversion] => Not used
# [format_spec] => [.precision][type]
# => .[2][f] => .2f # where f means Fixed-point notation
Going further, Format strings have the below syntax. As you can see there is a lot more that can be done.
Syntax: "{" [field_name] ["!" conversion] [":" format_spec] "}"
# let's understand what each field means...
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
arg_name ::= [identifier | digit+]
attribute_name ::= identifier
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
conversion ::= "r" | "s" | "a"
format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]
# Looking at the underlying fields under format_spec...
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= digit+
grouping_option ::= "_" | ","
precision ::= digit+
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
Refer https://docs.python.org/3/library/string.html#format-string-syntax
It seems that no one use dynamic formatter. To use dynamic formatter, use:
WIDTH = 7
PRECISION = 3
TYPE = "f"
v = 3
print(f"val = {v:{WIDTH}.{PRECISION}{TYPE}}")
Other format see: https://docs.python.org/3.6/library/string.html#format-specification-mini-language
Reference: Others SO Answer
Simply
a = 10.1234
print(f"{a:.1f}")
Output: 10.1
a = 10.1234
print(f"{a:.2f}")
Output: 10.12
a = 10.1234
print(f"{a:.3f}")
Output: 10.123
a = 10.1234
print(f"{a:.4f}")
Output: 10.1234
Just change the value after the decimal point sign which represent how may decimal point you want to print.
a = 10.1234
print(f"{a:0.2f}")
in 0.2f:
0 is telling python to put no limit on the total number of digits to
display
.2 is saying that we want to take only 2 digits after decimal
(the result will be same as a round() function)
f is telling that it's a float number. If you forget f then it will just print 1 less digit after the decimal. In this case, it will be only 1 digit after the decimal.
A detailed video on f-string for numbers
https://youtu.be/RtKUsUTY6to?t=606
Is there an easy way with Python f-strings to fix the number of digits after the decimal point? (Specifically f-strings, not other string formatting options like .format or %)
For example, let's say I want to display 2 digits after the decimal place.
How do I do that? Let's say that
a = 10.1234
Include the type specifier in your format expression:
>>> a = 10.1234
>>> f'{a:.2f}'
'10.12'
When it comes to float numbers, you can use format specifiers:
f'{value:{width}.{precision}}'
where:
value is any expression that evaluates to a number
width specifies the number of characters used in total to display, but if value needs more space than the width specifies then the additional space is used.
precision indicates the number of characters used after the decimal point
What you are missing is the type specifier for your decimal value. In this link, you an find the available presentation types for floating point and decimal.
Here you have some examples, using the f (Fixed point) presentation type:
# notice that it adds spaces to reach the number of characters specified by width
In [1]: f'{1 + 3 * 1.5:10.3f}'
Out[1]: ' 5.500'
# notice that it uses more characters than the ones specified in width
In [2]: f'{3000 + 3 ** (1 / 2):2.1f}'
Out[2]: '3001.7'
In [3]: f'{1.2345 + 4 ** (1 / 2):9.6f}'
Out[3]: ' 3.234500'
# omitting width but providing precision will use the required characters to display the number with the the specified decimal places
In [4]: f'{1.2345 + 3 * 2:.3f}'
Out[4]: '7.234'
# not specifying the format will display the number with as many digits as Python calculates
In [5]: f'{1.2345 + 3 * 0.5}'
Out[5]: '2.7344999999999997'
Adding to Robᵩ's answer: in case you want to print rather large numbers, using thousand separators can be a great help (note the comma).
>>> f'{a*1000:,.2f}'
'10,123.40'
And in case you want to pad/use a fixed width, the width goes before the comma:
>>> f'{a*1000:20,.2f}'
' 10,123.40'
Adding to Rob's answer, you can use format specifiers with f strings (more here).
You can control the number of decimals:
pi = 3.141592653589793238462643383279
print(f'The first 6 decimals of pi are {pi:.6f}.')
The first 6 decimals of pi are 3.141593.
You can convert to percentage:
grade = 29/45
print(f'My grade rounded to 3 decimals is {grade:.3%}.')
My grade rounded to 3 decimals is 64.444%.
You can do other things like print constant length:
from random import randint
for i in range(5):
print(f'My money is {randint(0, 150):>3}$')
My money is 126$
My money is 7$
My money is 136$
My money is 15$
My money is 88$
Or even print with a comma thousand separator:
print(f'I am worth {10000000000:,}$')
I am worth 10,000,000,000$
Consider:
>>> number1 = 10.1234
>>> f'{number1:.2f}'
'10.12'
Syntax:
"{" [field_name] ["!" conversion] [":" format_spec] "}"
Explanation:
# Let's break it down...
# [field_name] => number1
# ["!" conversion] => Not used
# [format_spec] => [.precision][type]
# => .[2][f] => .2f # where f means Fixed-point notation
Going further, Format strings have the below syntax. As you can see there is a lot more that can be done.
Syntax: "{" [field_name] ["!" conversion] [":" format_spec] "}"
# let's understand what each field means...
field_name ::= arg_name ("." attribute_name | "[" element_index "]")*
arg_name ::= [identifier | digit+]
attribute_name ::= identifier
element_index ::= digit+ | index_string
index_string ::= <any source character except "]"> +
conversion ::= "r" | "s" | "a"
format_spec ::= [[fill]align][sign][#][0][width][grouping_option][.precision][type]
# Looking at the underlying fields under format_spec...
fill ::= <any character>
align ::= "<" | ">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= digit+
grouping_option ::= "_" | ","
precision ::= digit+
type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
Refer https://docs.python.org/3/library/string.html#format-string-syntax
It seems that no one use dynamic formatter. To use dynamic formatter, use:
WIDTH = 7
PRECISION = 3
TYPE = "f"
v = 3
print(f"val = {v:{WIDTH}.{PRECISION}{TYPE}}")
Other format see: https://docs.python.org/3.6/library/string.html#format-specification-mini-language
Reference: Others SO Answer
Simply
a = 10.1234
print(f"{a:.1f}")
Output: 10.1
a = 10.1234
print(f"{a:.2f}")
Output: 10.12
a = 10.1234
print(f"{a:.3f}")
Output: 10.123
a = 10.1234
print(f"{a:.4f}")
Output: 10.1234
Just change the value after the decimal point sign which represent how may decimal point you want to print.
a = 10.1234
print(f"{a:0.2f}")
in 0.2f:
0 is telling python to put no limit on the total number of digits to
display
.2 is saying that we want to take only 2 digits after decimal
(the result will be same as a round() function)
f is telling that it's a float number. If you forget f then it will just print 1 less digit after the decimal. In this case, it will be only 1 digit after the decimal.
A detailed video on f-string for numbers
https://youtu.be/RtKUsUTY6to?t=606
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/
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.
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