Format truncated Python float as int in string - python

A quick no-brainer:
some_float = 1234.5678
print '%02d' % some_float # 1234
some_float = 1234.5678
print '{WHAT?}'.format(some_float) # I want 1234 here too
Note: {:.0f} is not an option, because it rounds (returns 1235 in this example).
format(..., int(some_float)) is exactly the thing I'm trying to avoid, please don't suggest that.

It's worth mentioning the built in behavior for how floats are rendered using the raw format strings. If you know in advance where your fractional part lies with respect to 0.5 you can leverage the format string you originally attempted but discovered it fell short from rounding side effects "{:0.0f}". Check out the following examples...
>>> "{:0.0f}".format(1.999)
'2'
>>> "{:0.0f}".format(1.53)
'2'
>>> "{:0.0f}".format(1.500)
'2'
>>> "{:0.0f}".format(1.33)
'1'
>>> "{:0.0f}".format(0.501)
'1'
>>> "{:0.0f}".format(0.5)
'0'
>>> "{:0.0f}".format(0.1)
'0'
>>> "{:0.0f}".format(0.001)
'0'
As you can see there's rounding behavior behind the scenes. In my case where I had a database converting ints to floats I knew I was dealing with a non fractional part in advance and only wanted to render in an html template the int portion of the float as a workaround. Of course if you don't know in advance the fractional part you would need to carry out a truncation operation of some sort first on the float.

It's possible to extend the standard string formatting language by extending the class string.Formatter:
class MyFormatter(Formatter):
def format_field(self, value, format_spec):
if format_spec == 't': # Truncate and render as int
return str(int(value))
return super(MyFormatter, self).format_field(value, format_spec)
MyFormatter().format("{0} {1:t}", "Hello", 4.567) # returns "Hello 4"

This works:
from math import trunc
some_float = 1234.5678
print '{:d}'.format(trunc(some_float))
=> 1234
Or just do this, for that matter:
print trunc(some_float)
=> 1234
I think it's an acceptable answer, it avoids the conversion to int. Notice that in this snippet: '%02d' % some_float an implicit conversion to int is happening, you can't avoid some sort of conversion for printing in the desired format.

This will work too:
some_float = 1234.5678
f = lambda x: str(x)[:str(x).find('.')]
print '{}'.format(f(some_float))
=> 1234
After doing a %timeit test it looks like this is a bit faster than the trunc method.

Related

Why does f-string literal not work here, whereas %()s formatting does?

I am trying to format my validator message with the min/max values in the actual validator.
Here's my Flask Form:
class MyForm(FlaskForm):
example = IntegerField(label=('Integer 0-10'),
validators=[InputRequired(), NumberRange(min=0, max=10, message="must be between %(min)s and %(max)s!")])
Using message="must be between %(min)s and %(max)s!" gives me the expected output:
must be between 0 and 10!
Whereas using message=f"must be between {min} and {max}!" gives me the output:
must be between <built-in function min> and <built-in function max>!
How can I use f-string formatting for my validator message? Is this something related to f-string evaluating at run-time? I don't fully understand the concept behind it, I just know it's the preferred way to string format.
The f-string literal is evaluated immediately, before being passed to IntegerField.
>>> foo = 3
>>> print(f'{foo}')
3
The other string contains literal %(...) substrings which are
used later with the % operator.
>>> print("%(foo)s")
%(foo)s
>>> print("%(foo)s" % {'foo': 3})
3
"must be between %(min)s and %(max)s!" is a string literal that Flask will later perform a search-and-replace on, while f"must be between {min} and {max}!" is a simpler and more efficient way to say "must be between " + str(min) + " and " + str(max) + "!". That evaluates to the string you described.
You must declare such variables, like
min = 1
max = 2
print(f"must be between {min} and {max}!")
But please consider to use somewhat different variable names to not shadow builtin functions.
Ok, I see it now, you wanted to use that as a kind of string template.

How to truncate and pad in python3 using just str.format?

I would like to do this:
'{pathname:>90}'.format(pathname='abcde')[-2:]
using string formatting instead of array indexing.
So the result would be 'de'
or in the case of pathname='e' the result would be ' e' with a space before e. If the index would be [2:] this question would be answered by How to truncate a string using str.format in Python?
I need this in the following example:
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO,style='{',format='{pathname:>90}{lineno:4d}{msg}')
logging.info('k')
The precision trick (using a precision format) doesn't work. Only works to truncate the end of the string.
a workaround would be to slice the string before passing it to str.format:
>>> '{pathname:>2}'.format(pathname='abcde'[-2:])
'de'
>>> '{pathname:>2}'.format(pathname='e'[-2:])
' e'
since you cannot control the arguments passed to format, you could create a subclass of str and redefine format so when it meets pathname in the keyword arguments it truncates, then calls original str.format method.
Small self-contained example:
class TruncatePathnameStr(str):
def format(self,*args,**kwargs):
if "pathname" in kwargs:
# truncate
kwargs["pathname"] = kwargs["pathname"][-2:]
return str.format(self,*args,**kwargs)
s = TruncatePathnameStr('##{pathname:>4}##')
print(s.format(pathname='abcde'))
that prints:
## de##
use it in your real-life example:
logging.basicConfig(stream=sys.stdout, level=logging.INFO,style='{',
format=TruncatePathnameStr('{pathname:>90}{lineno:4d}{msg}'))

How to format complex number in Python

In my Python script for the line
result = sp.solve(eqn)
I get the following output in the format similar to
result = 0.0100503358535014*I
I gather this means the solution is a complex number. So in this case it could also be seen as result = 0.01j.
I want to add formatting to my code to change result = 0.0100503358535014*I to something more like result = 0.01j. However I am finding issues trying to do this as I was trying to use isinstance to check if result was complex
if isinstance(result, complex):
print "Result is complex!"
... my code here...
However this loop is never entered (i.e 0.0100503358535014*I isn't classified as complex here).
What way should I write an if statement to check if result is given in the manner xxxxxxx*I correctly?
SymPy supports Python's built-in function complex():
>>> import sympy
>>> x = sympy.sqrt(-2)
>>> x
sqrt(2)*I
>>> complex(x)
1.4142135623730951j
There are some examples in http://docs.sympy.org/latest/modules/evalf.html
A Python complex number can be formatted similar to a float:
>>> format(complex(x), 'e')
'0.000000e+00+1.414214e+00j'
>>> format(complex(x), 'f')
'0.000000+1.414214j'
>>> format(complex(x), 'g')
'0+1.41421j'
If you want to format the real and imag parts separately you can do it yourself.
The conversion would raise a TypeError if it can't be done:
>>> complex(sympy.Symbol('x'))
Traceback (most recent call last):
TypeError: can't convert expression to float
Here's an alternative which incidentally indicates how to check whether one or both parts of a complex number are available.
>>> from sympy import *
>>> var('x')
x
>>> expr = (x+0.012345678*I)*(x-0.2346678934)*(x-(3-2.67893455*I))
>>> solve(expr)
[0.234667893400000, -0.012345678*I, 3.0 - 2.67893455*I]
>>> roots = solve(expr)
>>> for root in roots:
... r, i = root.as_real_imag()
... '%10.3f %10.3f i' % (r,i)
...
' 0.235 0.000 i'
' 0.000 -0.012 i'
' 3.000 -2.679 i'
You could check the sign of the complex part to decide whether to put a plus sign in. I would like to have been able to use the newfangled formatting but fell afoul of the bug which comes with Py3.4+ mentioned in Python TypeError: non-empty format string passed to object.__format__ for which I have no remedy.
The following checks whether the sympy object result represents a complex number and truncates the float value to 2 decimal points.
import sympy as sp
result = 0.0100503358535014*sp.I
print(result.is_complex)
print(sp.N(result,2))
True
0.01*I

Type Error while using variables

So i have this line of code:
fc = round((1+5*100)/100, 3) if fc_no_rocks == None else round(fc_no_rocks/100, 3)
that takes in a variable, whose type should be float. When I test the variable type using type(), it returns:
>>>type(fc_no_rocks)
<type 'float'>
but i keep getting an error that says "unsupported operand types for /: str and int.
Obviously, fc_no_rocks is a string in your case. That bug is on you. Better to check for several cases:
fc_no_rocks is a number
fc_no_rocks is a string indicating a number
fc_no_rocks is neither of the above
You check to make sure that fc_no_rocks isn't None, but it could be anything. So it's better to check more exclusively at first, and then let your else case be the catch-all, i.e. neither/none of the above.
In one big mess of a ternary chain, it's this:
fc = round(float(fc_no_rocks)/100.0, 3) if isinstance(fc_no_rocks, str) and unicode(fc_no_rocks.replace('.','',1)).isnumeric() else round(fc_no_rocks/100.0, 3) if isinstance(fc_no_rocks, float) or isinstance(fc_no_rocks, int) else round((1+5*100)/100.0, 3)
Better to write it out in multiple lines, imo, but one-liners are such fun to write. It's like putting a bucket of water on top of a door that you know someone else is going to walk through. It sucks to be the person maintaining your code...! (By the way, make sure that you quit your job after writing this sort of stuff so that you don't have to be the one maintaining it.)
Anyway, output:
>>> fc_no_rocks = "2.3"
>>> fc = ...
>>> fc
0.023
>>> fc_no_rocks = "foobar"
>>> fc = ...
>>> fc
5.01
>>> fc_no_rocks = 1.3
>>> fc = ...
>>> fc
0.013
>>> fc_no_rocks = 6340
>>> fc = ...
>>> fc
63.4
If you want to debug right in the middle of that statement, I have good news:
>>> import sys
>>> fc_no_rocks = "foobar"
>>> fc = round(float(fc_no_rocks)/100.0, 3) if sys.stdout.write(str(type(fc_no_rocks))+"\n") or isinstance(fc_no_rocks, str) and unicode(fc_no_rocks.replace('.','',1)).isnumeric() else round(fc_no_rocks/100.0, 3) if isinstance(fc_no_rocks, float) or isinstance(fc_no_rocks, int) else round((1+5*100)/100.0, 3)
<type 'str'>
>>> fc
5.01
You can abuse the boolean or operator's behavior and the fact that the write() method always returns None! Hooray! You can also write repr(fc_no_rocks) instead if you want to see its representation - useful for getting both the contents of a string and an indication that yes, it is a string.
Edit: I'm running Python 2.7.2, so I had to add the decimal points to divide correctly. Woops!
Why not just make sure fc_no_rocks is a float?
fc = round((1+5*100)/100, 3) if fc_no_rocks == None else round(float(fc_no_rocks)/100, 3)
There was a for loop that had changed the variables so the fc_no_rocks was set to None. This made the logic when setting the fc variable switch to the left, where one of the variables i had replaces was also a string. sorry for the mixup
There is one other thing.
round((1+5*100)/100, 3) there is nothing to round, because the numeric literals are all integers and the result of this integer equation (1+5*100)/100 which includes a division is 5, not 5.01 as you would expect. To repair it you have to use all decimal literals in every of your decimal calculations:
round((1.0+5.0*100.0)/100.0, 3)
EDIT: The repaired statement would result in 5.01, so you should be careful in other parts of your code.

Can we get the following flexibility in Python as In Perl

Sorry. I am not trying to start any flame. My scripting experience is from Perl, and I am pretty new in Python.
I just want to check whether I can have the same degree of flexibility as in Python.
In Python :
page = form.getvalue("page")
str = 'This is string : ' + str(int(page) + 1)
In Perl :
$str = 'This is string : ' . ($page + 1);
Is there any way I can avoid int / str conversion?
No, since Python is strongly typed. If you keep page as an int you can do the following:
s = 'This is string : %d' % (page + 1,)
You could use:
mystr = "This string is: %s" % (int(page) + 1)
... the string conversion will be automatic when interpolating into the %s via the % (string formating operator).
You can't get around the need to convert from string to integer. Python will never conflate strings for other data types. In various contexts Python can return the string or "representation" of an object so there are some implicit data casts into string forms. (Under the hood these call .__str__() or .__repr__() object methods).
(While some folks don't like it I personally think the notion of overloading % for string interpolation is far more sensible than a function named sprintf() (if you have a language with operator overloading support anyway).
It looks like page is a str
page = form.getvalue("page")
S = 'This is string : %d'%(int(page)+1)
otherwise make page an int
page = int(form.getvalue("page"))
S = 'This is string : %d'%(page+1)
For the record (and to show that this is nothing to do with strong typing), you can also do crazy stuff like this:
>>> class strplus(int):
... def __radd__(self, other):
... return str(int(other).__add__(self))
...
>>> page = form.getvalue("page")
>>> page + strplus(1)
'3'
No. Python doesn't have the same level of polymorphism as perl. You can print anything, and mix and match floats and ints quite easily, and lots of things (0, '', "", () and []) all end up False, but no, it's not perl in terms of polymorphism.

Categories