String formatting with “{0:d}” doesn't convert to integer - python

This is about the same issue as in this question about floats.
When you've got a value that could get converted to an integer, the old %d would convert it, but format doesn't.
class MyIntegerTenClass:
def __int__(self):
return 10
def __str__(self):
return 'ten'
ten = MyIntegerTenClass()
print '%d, %02X, %s' % (ten, ten, ten) # ok
print '{0}'.format(ten) # ok
print '{0:d}, {0:02X}'.format(ten) # ValueError: Unknown format code 'd' for object of type 'str'
Is there a way to modify the behaviour of format, without touching the class of the value to be formatted (without adding a __format__ method to that class)?
Edit: My goal is to get the formatting dependent on the format string, but not on the value.
So if the format string says "d" or "x", convert the value to int and then to decimal or hexadecimal representation.
If the format string says "s", convert it to string directly. As the old % did.
Actually, I could even add a __format__ method to the class of the value. But how do I check, in that method, if the given format specification is an integer format specification? Without reimplementing the format specification parser of the builtin format.
Edit: Here's a solution with __format__ and exceptions. Any better ideas?
class MyIntegerTenClass:
def __int__(self):
return 10
def __str__(self):
return 'ten'
def __format__(self, spec):
fmt = '{0:%s}'%spec
try:
return fmt.format(str(self))
except:
return fmt.format(int(self))
ten = MyIntegerTenClass()
print '%d, %02X, %s' % (ten, ten, ten) # ok, prints "10, 0A, ten"
print '{0:d}, {0:02X}, {0}'.format(ten) # ok, prints "10, 0A, ten"

A first approach to this problem might be simply to try it:
class MyIntegerTenClass:
def __int__(self):
return 10
def __str__(self):
return 'ten'
def __format__(self, format_spec):
try:
s = format(str(self), format_spec)
except ValueError:
s = format(int(self), format_spec)
return s
If MyIntegerTenClass can be inserted into the format string as a string, it will be. If not, it will be converted into an int and resubmitted to format.
>>> print '{0}, {0:s}, {0:d}, {0:02X}, {0:f}'.format(ten)
ten, ten, 10, 0A, 10.000000
If you want the default representation to be 10 instead of ten, you need only swap the conversion lines:
def __format__(self, format_spec):
try:
s = format(int(self), format_spec)
except ValueError:
s = format(str(self), format_spec)
return s
Test output:
>>> print '{0}, {0:s}, {0:d}, {0:02X}, {0:f}'.format(ten)
10, ten, 10, 0A, 10.000000
As an addendum, I don't believe you'll be able to get the behavior you want without defining __format__; however, you can develop a more sophisticated approach using a Formatter object. Still, I think the exception-based approach gives you a lot of built-in functionality for free.

pass an integer instead of a string and it'll work fine.
>>> print '{0:d}'.format(1)
1
>>> print '{0:d}'.format('1')
Traceback (most recent call last):
File "<pyshell#57>", line 1, in <module>
print '{0:d}'.format('1')
ValueError: Unknown format code 'd' for object of type 'str'

It says ten is a string, you are trying to format it as a number.
Try wrapping it in a cast:
>>> print '{0:d}, {0:02X}'.format(int(ten))
10, 0A

Related

Display trailing zeros in a decimal number in python without returning a string

I need to display trailing zeros in a decimal number. I have consider using python format() but it returns a string and when I convert it to float or decimal the trailing zero disappears.
I have also tried using python decimal but it returns a Decimal instance on variable assignment.
For example:
>>> x = decimal.Decimal(format(15.2, '.2f'))
>>> print x
15.20
>>> x
Decimal('15.20')
Python version: 2.7
I need to display 15.2 as 15.20 (not as '15.20' << string type)
Thanks In Advance
You can create a float subclass that behaves this way:
class RoundFloat(float):
def __new__(cls, value=0, places=2):
return float.__new__(cls, value)
def __init__(self, value=0, places=2):
self.places = str(places)
def __str__(self):
return ("%." + self.places + "f") % self
__repr__ = __str__
Usage:
pi = RoundFloat(3.1415927, places=4)
print pi # 3.1416
It may be convenient to leave off the __repr__ so you can easily get the full value if you need it using repr(x).

Creating new conversion specifier in Python

In python we have conversion specifier like
'{0!s}'.format(10)
which prints
'10'
How can I make my own conversion specifiers like
'{0!d}'.format(4561321)
which print integers in following format
4,561,321
Or converts it into binary like
'{0!b}'.format(2)
which prints
10
What are the classes I need to inherit and which functions I need to modify? If possible please provide a small example.
Thanks!!
What you want to do is impossible, because built-in types cannot be modified and literals always refer to built-in types.
There is a special method to handle the formatting of values, that is __format__, however it only handles the format string, not the conversion specifier, i.e. you can customize how {0:d} is handled but not how {0!d} is. The only things that work with ! are s and r.
Note that d and b already exist as format specifiers:
>>> '{0:b}'.format(2)
'10'
In any case you could implement your own class that handles formatting:
class MyInt:
def __init__(self, value):
self.value = value
def __format__(self, fmt):
if fmt == 'd':
text = list(str(self.value))
elif fmt == 'b':
text = list(bin(self.value)[2:])
for i in range(len(text)-3, 0, -3):
text.insert(i, ',')
return ''.join(text)
Used as:
>>> '{0:d}'.format(MyInt(5000000))
5,000,000
>>> '{0:b}'.format(MyInt(8))
1,000
Try not to make your own and try to use default functions already present in python. You can use,
'{0:b}'.format(2) # for binary
'{0:d}'.format(2) # for integer
'{0:x}'.format(2) # for hexadecimal
'{0:f}'.format(2) # for float
'{0:e}'.format(2) # for exponential
Please refer https://docs.python.org/2/library/string.html#formatspec for more.

Avoiding the mistake of comparing number and string

We've all made this kind of mistake in python:
if ( number < string ):
python silently accepts this and just gives incorrect output.
Thank goodness python 3 finally warns us. But in some cases python 2.7 is needed. Is there any way in python 2.7 to guard against this mistake other than "just be careful" (which we all know doesn't work 100% of the time)?
You could explicitly convert both numbers to int. The string will get converted, and the number won't be effected (it's already an int). So this saves you the need to start remembering what type of value the number holds:
a = 11
b = "2"
print a > b # prints False, which isn't what you intended
print int(a) > int(b) # prints True
EDIT:
As noted in the comments, you cannot assume a number is an integer. However, applying the same train of though with the proper function - float should work just fine:
a = 11
b = "2"
print a > b # prints False, which isn't what you intended
print float(a) > float(b) # prints True
If you really, really want to be 100% sure that comparing strings and ints is impossible, you can overload the __builtin__.int (and __builtin__.float, etc. as necessary) method to disallow comparing ints (and floats, etc) with strings. It would look like this:
import __builtin__
class no_str_cmp_int(int):
def __lt__(self,other):
if type(other) is str:
raise TypeError
return super.__lt__(other)
def __gt__(self,other):
if type(other) is str:
raise TypeError
return super.__gt__(other)
# implement __gte__, __lte__ and others as necessary
# replace the builtin int method to disallow string comparisons
__builtin__.int = no_str_cmp_int
x = int(10)
Then, if you attempted to do something like this, you'd receive this error:
>>> print x < '15'
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
print x < '15'
File "tmp.py", line 7, in __lt__
raise TypeError
TypeError
There is a major caveat to this approach, though. It only replaces the int function, so every time you created an int, you'd have to pass it through the function, as I do in the declaration of x above. Literals will continue to be the original int type, and as far as I am aware there is no way to change this. However, if you properly create these objects, they will continue to work with the 100% assurance you desire.
Just convert the string or any data type to float first.
When two data types are same, then only we can compare them.
Suppose,
a = "10"
b= 9.3
c=9
We want to add a,b,c.. So,
So, the correct way to add these three is to convert them to same data type and then add.
a = float(a)
b = float(b)
c = float(c)
print a+b+c
You can check if each variable is an int like this :
if ( isinstance(number, int) and isinstance(string, int) ):
if (number < string):
Do something
else:
Do something else
else :
print "NaN"
*Edit:
To check for a float too the code should be :
if ( isinstance(number, (int,float )) and isinstance(string, (int,float) ) ):

Python string format: When to use !s conversion flag

What's the difference between these 2 string format statements in Python:
'{0}'.format(a)
'{0!s}'.format(a)
Both have the same output if a is an integer, list or dictionary. Is the first one {0} doing an implicit str() call?
Source
PS: keywords: exclamation / bang "!s" formatting
It is mentioned in the documentation:
The conversion field causes a type coercion before formatting.
Normally, the job of formatting a value is done by the __format__()
method of the value itself. However, in some cases it is desirable to
force a type to be formatted as a string, overriding its own
definition of formatting. By converting the value to a string before
calling __format__(), the normal formatting logic is bypassed.
Two conversion flags are currently supported: '!s' which calls
str() on the value, and '!r' which calls repr().
An example can be taken (again from the documentation) to show the difference:
>>> "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
"repr() shows quotes: 'test1'; str() doesn't: test2"
Simply said:
'{0}'.format(a) will use the result of a.__format__() to display the value
'{0!s}'.format(a) will use the result of a.__str__() to display the value
'{0!r}'.format(a) will use the result of a.__repr__() to display the value
>>> class C:
... def __str__(self): return "str"
... def __repr__(self): return "repr"
... def __format__(self, format_spec): return "format as " + str(type(format_spec))
...
>>> c = C()
>>> print "{0}".format(c)
format as <type 'str'>
>>> print u"{0}".format(c)
format as <type 'unicode'>
>>> print "{0!s}".format(c)
str
>>> print "{0!r}".format(c)
repr
Concerning the second argument of __format__, to quote PEP 3101 "Controlling Formatting on a Per-Type Basis":
The 'format_spec' argument will be either
a string object or a unicode object, depending on the type of the
original format string. The __format__ method should test the type
of the specifiers parameter to determine whether to return a string or
unicode object. It is the responsibility of the __format__ method
to return an object of the proper type.
Thanks to the comment & answer from #hjpotter92 for explanation:
Here's an example that shows the difference (it's when you override the __format__ method)
class MyClass:
i = 12345
def __format__(self, i):
return 'I Override'
>>> obj = MyClass()
>>> '{0}'.format(obj)
'I Override'
>>> '{0!s}'.format(obj)
'<__main__.MyClass instance at 0x021AA6C0>'

how % applies to this method in Python?

From my studying of python, I've found two uses for %. It can be used as what's called a modulo, meaning it will divide the value to the left of it and the value to the right of it and spit back the remainder.
The other use is a string formatter. So I can do something like 'Hi there %s' % name, where name is a list of names.
Also, if you see %% in a string formatting, that means a literal % will be entered.
Here is my question, I found this:
class FormatFormatStr(FormatObj):
def __init__(self, fmt):
self.fmt = fmt
def tostr(self, x):
if x is None: return 'None'
return self.fmt%self.toval(x)
What does return self.fmt%self.toval(x) mean? It can't be a modulo because toval will give me a string. It's not really a string formatter because there isn't another percent sign.
also, related to this:
def csvformat_factory(format):
format = copy.deepcopy(format)
if isinstance(format, FormatFloat):
format.scale = 1. # override scaling for storage
format.fmt = '%r'
return format
What does the percent mean in format.fmt = '%r' does this mean to insert a string a la repr()? Or does it mean insert what the variable r represents? r in this overall program also refers to a recarray.
Thanks everyone. Hope this makes sense =)
The string % operator is simpler than you are imagining. It takes a string on the left side, and a variety of things on the right side. The left side doesn't have to be a literal string, it can be a variable, or the result of another computation. Any expression that results in a string is valid for the left side of the %.
In your first example, self.fmt is a string. In order to be useful in this context, it should have a percent sign in it.
In your second example, format.fmt is being set to a string that would be useful as the left side of the %. In this case, "%r" means, insert the repr() of the value into the string, as you have said.
In
return self.fmt%self.toval(x)
self.fmt is a string, and that string presumably has a percent-sign placeholder in it.
%r in a format string is like %s but it prints the repr() of the string, so it'll have quotes and backslashes and all that.
% is just an operator which is just a method, and like any other method you can either pass in a literal value or a variable containing a value. In your examples they use a variable containing the format string.
def tostr(self, x):
if x is None: return 'None'
return self.fmt%self.toval(x)
The % in this is a string formatter, definitely. Pass the tostr method a formatter, eg "%s" or "%r" to see what happens
I think the '%r' in csvformat_factory is also a string formatter. '%r' means take the repr() which is a reasonable way to display something to a user. I imagine that format.fmt is used elsewhere format.fmt % somevalue.
The code:
return self.fmt % self.toval(x)
Is the "string formatting" use of the % operator, just like you suspected.
The class is handed format, which is a string containing the formatting, and when tostr(x) is called, it will return the string % x.
This is just like using % directly, only with saving the format string for later. In other words, instead of doing:
"I want to print the number: %n" % 20
What's happening is:
format_str = "I want to print the number: %n"
x = 20
print format_str % x
Which is exactly the same thing.
% has more than one use in string formatting. One use is in %s, %d, etc.
Another use is to separate 'string in which we use %d and %s' from int-value and string-value.
For example
'string in which we use %d and %s' % (17, 'blue')
would result in
'string in which we use 17 and blue'
we could store 'string in which we use %d and %s' in a variable,
a = 'string in which we use %d and %s'
then
a % (17, 'blue')
results in
'string in which we use 17 and blue'
In your example
self.fmt%self.toval(x)
self.fmt is similar to a above and self.toval(x) is (17, 'blue')

Categories

Resources