set_9 = {1,2,3,4,5}
for a in set_9:
print(f"{a:>6.2f}")
Output
1.00
2.00
3.00
4.00
5.00
Is :> an operator? If not, what type is it?
What is 6.2f mean?
The format used is called Formatted string literals or f-strings, which is f"{a:>6.2f}"
Anything inside the curly braces is replacement field, which is {a:>6.2f}
Here, a is the field_name and >6.2f is the format_spec.
replacement_field ::= "{" [field_name] ["!" conversion] [":"
format_spec] "}"
Expanding the format specification used in your example:
> - align - Right align the field
6 - width - Minimum width (by default space is used as
fill character)
.2 - precision - Number of digits after the decimal point
f - type - Floating point notation
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
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.
I've made some progress thanks to feedback from this forum ( thanks forum!).
The pyparsing.Dict object dict is getting populated but silently fails when it finds decimal numbers.
given:
import pyparsing as pp
lines = '''\
(rate multiple)
(region "mountainous")
(elev 21439)
(alteleva +21439)
(altelevb -21439)
(coorda 23899.747)
(coordb +23899.747)
(coordc -23899.747)
(coordd 853.324e21)
(coorde +853.324e21)
(coordf -853.324e21)
(coordg 987.88e+09)
(coordh +987.88e+09)
(coordi -987.88e+09)
(coordj 122.45e-04)
(coordk +122.45e-04)
(coordl -122.45e-04)
'''
leftParen = pp.Literal('(')
rightParen = pp.Literal(')')
colon = pp.Literal(':')
decimalpoint = pp.Literal('.')
doublequote = pp.Literal('"')
plusorminus = pp.Literal('+') | pp.Literal('-')
exp = pp.CaselessLiteral('E')
v_string = pp.Word(pp.alphanums)
v_quoted_string = pp.Combine( doublequote + v_string + doublequote)
v_number = pp.Regex(r'[+-]?(?P<float1>\d+)(?P<float2>\.\d+)?(?P<float3>[Ee][+-]?\d+)?')
keyy = v_string
valu = v_string | v_quoted_string | v_number
item = pp.Group( pp.Literal('(').suppress() + keyy + valu + pp.Literal(')').suppress() )
items = pp.ZeroOrMore( item)
dict = pp.Dict( items)
print "dict yields: ", dict.parseString( lines).dump()
yields
- alteleva: '+21439',
- altelevb: '-21439',
- elev: '21439',
- rate: 'multiple',
- region: '"mountainous"'
Changing the order of tokens around proves the script silently fails when it hits the first decimal number, which implies there's something subtly wrong with the pp.Regex statement but I sure can't spot it.
TIA,
code_warrior
Your problem actually lies in this expression:
valu = v_string | v_quoted_string | v_number
Because v_string is defined as the very broadly-matching expression:
v_string = pp.Word(pp.alphanums)
and because it is the first expression in valu, it will mask v_numbers that start with a digit. This is because the '|' operator produces pp.MatchFirst objects, so the first expression matched (reading left-to-right) will determine which alternative is used. You can convert to using the '^' operator, which produces pp.Or objects - the Or class will try to evaluate all the alternatives and then go with the longest match. However, note that using Or carries a performance penalty, since many more expressions are test for a match even when there is no chance for confusion. In your case, you can just reorder the expressions to put the least specific matching expression last:
valu = v_quoted_string | v_number | v_string
Now values will be parsed first attempting to parse as quoted strings, then as numbers, and then only if there is no match for either of these specific types, as the very general type v_string.
A few other comments:
I personally prefer to parse quoted strings and only get the content within the quotes (It's a string, I know it already!). There used to be some confusion with older versions of pyparsing when dumping out the parsed results when parsed strings were displayed without any enclosing quotes. But now that I use repr() to show the parsed values, strings show up in quotes when calling dump(), but the value itself does not include the quotes. When it is used elsewhere in the program, such as saving to a database or CSV, I don't need the quotes, I just want the string content. The QuotedString class takes care of this for me by default. Or use pp.quotedString().addParseAction(pp.removeQuotes).
A recent pyparsing release introduced the pyparsing_common namespace class, containing a number of helpful pre-defined expressions. There are several for parsing different numeric types (integer, signed integer, real, etc.), and a couple of blanket expressions: number will parse any numeric type, and produce values of the respective type (real will give a float, integer will give an int, etc.); fnumber will parse various numerics, but return them all as floats. I've replaced your v_number expression with just pp.pyparsing_common.number(), which also permits me to remove several other partial expressions that were defined just for building up the v_number expression, like decimalpoint, plusorminus and exp. You can see more about the expressions in pyparsing_common at the online docs: https://pythonhosted.org/pyparsing/
Pyparsing's default behavior when processing literal strings in an expression like "(" + pp.Word(pp.alphas) + valu + ")" is to automatically convert the literal "(" and ")" terms to pp.Literal objects. This prevents accidentally losing parsed data, but in the case of punctuation, you end up with many cluttering and unhelpful extra strings in the parsed results. In your parser, you can replace pyparsing's default by calling pp.ParserElement.inlineLiteralsUsing and passing the pp.Suppress class:
pp.ParserElement.inlineLiteralsUsing(pp.Suppress)
Now you can write an expression like:
item = pp.Group('(' + keyy + valu + ')')
and the grouping parentheses will be suppressed from the parsed results.
Making these changes, your parser now simplifies to:
import pyparsing as pp
# override pyparsing default to suppress literal strings in expressions
pp.ParserElement.inlineLiteralsUsing(pp.Suppress)
v_string = pp.Word(pp.alphanums)
v_quoted_string = pp.QuotedString('"')
v_number = pp.pyparsing_common.number()
keyy = v_string
# define valu using least specific expressions last
valu = v_quoted_string | v_number | v_string
item = pp.Group('(' + keyy + valu + ')')
items = pp.ZeroOrMore( item)
dict_expr = pp.Dict( items)
print ("dict yields: ", dict_expr.parseString( lines).dump())
And for your test input, gives:
dict yields: [['rate', 'multiple'], ['region', 'mountainous'], ['elev', 21439],
['alteleva', 21439], ['altelevb', -21439], ['coorda', 23899.747], ['coordb',
23899.747], ['coordc', -23899.747], ['coordd', 8.53324e+23], ['coorde',
8.53324e+23], ['coordf', -8.53324e+23], ['coordg', 987880000000.0], ['coordh',
987880000000.0], ['coordi', -987880000000.0], ['coordj', 0.012245], ['coordk',
0.012245], ['coordl', -0.012245]]
- alteleva: 21439
- altelevb: -21439
- coorda: 23899.747
- coordb: 23899.747
- coordc: -23899.747
- coordd: 8.53324e+23
- coorde: 8.53324e+23
- coordf: -8.53324e+23
- coordg: 987880000000.0
- coordh: 987880000000.0
- coordi: -987880000000.0
- coordj: 0.012245
- coordk: 0.012245
- coordl: -0.012245
- elev: 21439
- rate: 'multiple'
- region: 'mountainous'
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'm writing a profile manager for Stellaris game and I've hit a wall with their format in which they keep the info about mods and settings.
Mod file:
name="! (Ship Designer UI Fix) !"
path="mod/ship_designer_ui_fix"
tags={
"Fixes"
}
remote_file_id="879973318"
supported_version="1.6"
Settings:
language="l_english"
graphics={
size={
x=1920
y=1200
}
min_gui={
x=1920
y=1200
}
gui_scale=1.000000
gui_safe_ratio=1.000000
refreshRate=59
fullScreen=no
borderless=no
display_index=0
shadowSize=2048
multi_sampling=8
maxanisotropy=16
gamma=50.000000
vsync=yes
}
last_mods={
"mod/ship_designer_ui_fix.mod"
"mod/ugc_720237457.mod"
"mod/ugc_775944333.mod"
}
I've thought pyparsing will be of help there (and it probably will be) but it has been a long time since I've actually did something like this and this I'm clueless atm.
I've got to extract the simple key=value but I'm struggling to actually move from there to be able to extract the arrays, not to mention the multilevel arrays.
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
equals = Literal("=").suppress()
nonequals = "".join([c for c in printables if c != "="]) + " \t"
keydef = ~lbrack + Word(nonequals) + equals + restOfLine
conf = Dict( ZeroOrMore( Group(keydef) ) )
tokens = conf.parseString(data)
I haven't got very far as you can see. Can anyone point me towards next step? I'm not asking a finished and working solution for the whole thing - it would move me forward a lot but where's the fun in that :)
Well, it is awfully tempting to just dive in and write this parser, but you want some of that fun for yourself, that's great.
Before writing any code, write a BNF. That way you'll write a decent and robust parser, instead of just "everything that's not an equals sign must be an identifier".
There are a lot of "something = something" bits here, look at the kinds of things on the right- and left-hand sides of the '='. The left-hand sides all look like pretty well-mannered identifiers: alphas, underscores. I could envision numeric digits too, as long as they aren't the leading character. So let's say the left-hand sides will be identifiers:
identifier_leading = 'A'..'Z' 'a'..'z' '_'
identifier_body = identifier_leading '0'..'9'
identifier ::= identifier_leading + identifier_body*
The right-hand sides are a mix of things:
integers
floats
'yes' or 'no' booleans
quoted strings
something in braces
The "something in braces" are either a list of quoted strings, or a list of 'identifer = value' pairs. I'll skip the awful details of defining floats and integers and quoted strings, let's just assume we have those defined:
boolean_value ::= 'yes' | 'no'
value ::= float | integer | boolean_value | quoted_string | string_list_in_braces | key_value_list_in_braces
string_list_in_braces ::= '{' quoted_string * '}'
key_value ::= identifier '=' value
key_value_list_in_braces ::= '{' key_value* '}'
You will have to use a pyparsing Forward to declare value before it is fully defined, since it is used in key_value, but key_value is used in key_value_list_in_braces, which is used to define value - a recursive grammar. You are already familiar with the Dict(OneOrMore(Group(named_item))) pattern, and this should be good to give you a structure of fields that are accessible by name. For identifier, a Word would work, or you could just use the pre-defined pyparsing_common.identifier which was introduced as part of the pyparsing_common namespace class last year.
The translation from BNF to pyparsing should be pretty much 1-to-1 from here. For that matter, from the BNF, you could use PLY, ANTLR, or another parsing lib too. The BNF is really worth taking the 1/2 hour or 1/2 day to get sorted out.