Python string format with incorrect values - python

I have a string that I'd like to format but the values I'm using to format may or may not be proper values (None, or ''). In any event that one of these improper values is passed, I still want the string to format, but ignoring any values that will not work. For example:
mystring = "{:02d}:{:02d}"
mystring.format('', 1)
In this case I'd like my output to be :01, thus negating the fact that '' won't work for the first value in the string. I looked at something like
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"
But as I'm not using a dictionary for values, I don't think this solution will work for me.

You could write your own formatter and override format_field() to catch these errors and just returns empty strings. Here's the basics (you might want to edit to only catch certain errors):
import string
class myFormat(string.Formatter):
def format_field(self, value, format_spec):
try:
return super().format_field(value, format_spec)
except ValueError:
return ''
fmt = myFormat()
mystring = "{:02d}:{:02d}"
print(fmt.format(mystring, *(2, 1)))
# 02:01
print(fmt.format(mystring, *('', 1)))
# :01

You could use a try-except to check if each value is valid:
out = []
for v in ['', 1]:
# Check if v can be converted to a decimal number
try:
out.append(format(v, '02d'))
except (ValueError, TypeError):
out.append('')
print(':'.join(out)) # -> :01

Related

Error while working with construction try-except ValueError

result = []
try:
for i in range(len(ass)):
int(df['sku'][i])
except ValueError:
result.append(df['sku'][i])
I need to collect all the errors in a list. Tell me, please, the code above adds only the first error, I need everything.
After iterating over all sku values, only those that cannot be converted to int should be included in the list.
You can move the try...except inside the loop:
result = []
for i in range(len(ass)):
try:
int(df['sku'][i])
except ValueError:
result.append(df['sku'][i])
You can also use isdigit() with a list comprehension as follows:
result = [val for val in df['sku'] if val.isdigit()]
However, you should note that isdigit() will not work in some cases e.g. those with leading signs.
As an example, '+1' will convert to an integer type fine with int() but will return False with is isdigit(). Similarly, -1 will convert fine with int() but return False with isdigit().
Further information can be found int the documentation:
str.isdigit()
Return true if all characters in the string are digits and there is at least one character, false otherwise.
You'd want the try-except in the loop:
result = []
for i in range(len(ass)):
try:
int(df['sku'][i])
except ValueError:
result.append(df['sku'][i])
But if it's really a list of non-digit SKUs you want,
result = [sku for sku in df['sku'] if not sku.isdigit()]
This should work:
result = []
for i in range(len(ass)):
try:
int(df['sku'][i])
except ValueError:
result.append(df['sku'][i])

How to check if the contents of an entry widget in python/tk is a float, string, boolean or integer?

I am trying to check whether users' input to a Tk GUI I created is of the right data type I wanted it to be (integer), but I have only been able to check whether their input is a Boolean, and I need to check whether their input is a string or an integer as well:
from tkinter import*
# creates a GUI
Tester = Tk()
NonEssentialFoodEntry = Entry(Tester, width="30")
NonEssentialFoodEntry.place(x=300,y=540)
def checker():
if NonEssentialFoodEntry.get() == 'TRUE' or 'FALSE':
tkinter.messagebox.showerror("","You have entered a boolean value in the Non-EssentialFood entry field, please enter an integer")
Checker=Button(Tester, height="7",width="30",font=300,command=checker)
Checker.place(x=700, y=580)
Well, you could run a regular expression on the input and check which group is not None:
(?:^(?P<boolean>TRUE|FALSE)$)
|
(?:^(?P<integer>\d+)$)
|
(?:^(?P<float>\d+\.\d+)$)
|
(?:^(?P<string>.+)$)
See a demo on regex101.com. In the first place, every input is a string though.
In Python:
import re
strings = ["TRUE", "FALSE", "123", "1.234343", "some-string", "some string with numbers and FALSE and 1.23 in it"]
rx = re.compile(r'''
(?:^(?P<boolean>TRUE|FALSE)$)
|
(?:^(?P<integer>-?\d+)$)
|
(?:^(?P<float>-?\d+\.\d+)$)
|
(?:^(?P<string>.+)$)
''', re.VERBOSE)
for string in strings:
m = rx.search(string)
instance = [k for k,v in m.groupdict().items() if v is not None]
print(instance)
if instance:
print("{} is probably a(n) {}".format(string, instance[0]))
As said in the comments above your original question you might follow another way with try/except though.
One approach to this is to try to convert your input and see if it manages.
EDIT: this is basically the approach suggested by #martineau in the comments
The following code is adapted from FlyingCircus (Disclaimer: of which I am the main author):
def auto_convert(
text,
casts=(int, float, complex)):
"""
Convert value to numeric if possible, or strip delimiters from string.
Args:
text (str|int|float|complex): The text input string.
casts (Iterable[callable]): The cast conversion methods.
Returns:
val (int|float|complex): The numeric value of the string.
Examples:
>>> auto_convert('<100>', '<', '>')
100
>>> auto_convert('<100.0>', '<', '>')
100.0
>>> auto_convert('100.0+50j')
(100+50j)
>>> auto_convert('1e3')
1000.0
>>> auto_convert(1000)
1000
>>> auto_convert(1000.0)
1000.0
"""
if isinstance(text, str):
val = None
for cast in casts:
try:
val = cast(text)
except (TypeError, ValueError):
pass
else:
break
if val is None:
val = text
else:
val = text
return val
Note that you would need a specialized function for the Boolean case, since bool(text) will evaluate to True as soon as text is non-empty (which is also present in the latest version of FlyingCircus as flyingcircus.util.to_bool()).
For instance you could use the following code to build an if-statement around a valid integer:
if isinstance(<var>, int):
Otherwise get the type with type(<var>) and build your functions around it.

Python: Extract multiple float numbers from string

Forgive me, I'm new to Python.
Given a string that starts with a float of indeterminate length and ends with the same, how can I extract both of them into an array, or if there is just one float, just the one.
Example:
"38.00,SALE ,15.20"
"69.99"
I'd like to return:
[38.00, 15.20]
[69.99]
You could also use regex to do this
import re
s = "38.00,SALE ,15.20"
p = re.compile(r'\d+\.\d+') # Compile a pattern to capture float values
floats = [float(i) for i in p.findall(s)] # Convert strings to float
print floats
Output:
[38.0, 15.2]
def extract_nums(text):
for item in text.split(','):
try:
yield float(item)
except ValueError:
pass
print list(extract_nums("38.00,SALE ,15.20"))
print list(extract_nums("69.99"))
[38.0, 15.2]
[69.99]
However by using float conversion you are losing precision, If you want to keep the precision you can use decimal:
import decimal
def extract_nums(text):
for item in text.split(','):
try:
yield decimal.Decimal(item)
except decimal.InvalidOperation:
pass
print list(extract_nums("38.00,SALE ,15.20"))
print list(extract_nums("69.99"))
[Decimal('38.00'), Decimal('15.20')]
[Decimal('69.99')]
You said you're only interested in floats at the start and end of the string, so assuming it's comma-delimited:
items = the_string.split(',')
try:
first = float(items[0])
except (ValueError, IndexError):
pass
try:
second = float(items[-1])
except (ValueError, IndexError):
pass
We have to wrap the operations in exception handlers since the value might not be a valid float (ValueError) or the index might not exist in the list (an IndexError).
This will handle all cases including if one or both of the floats is omitted.
You can try something like
the_string = "38.00,SALE ,15.20"
floats = []
for possible_float in the_string.split(','):
try:
floats.append (float (possible_float.strip())
except:
pass
print floats
Try a list comprehension:
just_floats = [i for i in your_list.split(',') if i.count('.') == 1]
first you split the string where the commas are, then you filter through the string and get rid of values that don't have a decimal place
import re
map(float, filter(lambda x: re.match("\s*\d+.?\d+\s*", x) , input.split(","))
Input : input = '38.00,SALE ,15.20' Output: [38.0, 15.2]
Input : input = '38.00,SALE ,15.20, 15, 34.' Output: [38.0, 15.2, 15.0, 34.0]
Explanation:
Idea is to split the string : list_to_filter = input.split(",") splits on ,
Then using regex filter strings which are real numbers: filtered_list = filter(<lambda>, list_to_filter) , here item is included in output of filter if lamda expression is true. So when re.match("\s*\d+.?\d+\s*", x) matches for string x filter keeps it.
And finally convert into float. map(float, filtered_list). What it does is apply float() function to each element of the list
Split the input, check if each element is numeric when the period is removed, convert to float if it is.
def to_float(input):
return [float(x) for x in input.split(",") if unicode(x).replace(".", "").isdecimal()]

Better / More pythonic way to process the results of a function with multiple return values

The code below works, but looks very ugly. I'm looking for a more pythonic way to write the same thing.
The goal:
React on a result of a function that returns multiple values.
Example function
def myfilterfunc(mystr):
if 'la' in mystr:
return True, mystr
return False, None
This returns True and a string (if the string cointains "la"), or False and nothing.
In a second function, I'm passing myfilterfunc as an optional parameter
def mymainfunc(mystr,filterfunc=None):
This function fills a returnlist.
If no function is given, the result is not filtered and added as is.
If a filter function is given, if the filter function returns
True, a returned string is added. (This is just an example that would
easily work with one return value, but I'm trying to get the systax
right for a more complicated setup)
if filterfunc:
tmp_status,tmp_string = filterfunc(mystr[startpos:nextitem])
if tmp_status:
returnlist.append(tmp_string)
else:
returnlist.append(mystr[startpos:nextitem])
Any idea how I can write this without using temporary variables to store the return values of the function?
Full "working" test code below
def string2list(mystr,splitlist,filterfunc=None):
returnlist = []
startpos = 0
nextitem = -1
matched = True
while matched:
matched = False
for sub in splitlist:
if startpos == 0:
tmpi = mystr.find(sub)
else:
tmpi = mystr.find(sub,startpos + 1)
if (tmpi > 0) and ((nextitem < 0) or (nextitem > tmpi)):
nextitem = tmpi
matched = True
if filterfunc:
tmp_status,tmp_string = filterfunc(mystr[startpos:nextitem])
if tmp_status:
returnlist.append(tmp_string)
else:
returnlist.append(mystr[startpos:nextitem])
startpos = nextitem
nextitem = -1
return returnlist
def myfilterfunc(mystr):
if 'la' in mystr:
return True,mystr
return False,''
splitlist = ['li','la']
mytext = '''
li1
li2
li3
fg4
fg5
fg6
la7
la
la
tz
tz
tzt
tz
end
'''
print string2list(mytext,splitlist)
print
print string2list(mytext,splitlist,myfilterfunc)
If this is going to happen often you can factor out the uglyness:
def filtered(f, x):
if f:
status, result = f(x)
return result if status else x
else:
return x
used like
returnlist.append(filtered(filterfunc, mystr[startpos:nextitem]))
so that if you have many similar optional filters the code remains readable. This works because in Python functions/closures are first class citizens and you can pass them around like other values.
But then if the logic is about always adding (either the filtered or the unfiltered) why not just write the filter to return the input instead of (False, "") in case of failure?
That would make the code simpler to understand...
returnlist.append(filterfunc(mystr[startpos:nextitem]))
I think there are two better approaches to your problem that don't involve using two return values.
The first is to simply return a Boolean value and not a string at all. This works if your filter is always going to return the string it was passed unmodified if it returns a string at all (e.g. if the first value is True). This approach will let you avoid using temporary values at all:
if filterfunc:
if filterfunc(mystr[startpos:nextitem]):
returnlist.append(mystr[startpos:nextitem])
(Note, I'd suggest renaming filterfunc to predicate if you go this route.)
The other option will work if some filterfunc might return a different second value than it was passed under some situations, but never the 2-tuple True, None. In this approach you simply use the single value as both the signal and the payload. If it's None, you ignore it. If it's anything else, you use it. This does require a temporary variable, but only one (and it's a lot less ugly).
if filterfunc:
result = filterfunc(mystr[startpos:nextitem])
if result is not None:
returnlist.append(result)

What Might Cause Python Error 'str' object does not support item assignment

I'm trying to debug some Python 2.7.3 code to loop through a list of items and convert each to a string:
req_appliances = ['9087000CD', 'Olympus', 185]
for i in range(0, len(req_appliances)):
req_appliances[i] = str(req_appliances[i])
print req_appliances
The output is as follows:
['9087000CD', 'Olympus', '185']
In the example above, I've set the value of req_appliances explicitly to test the loop. In the actual code, req_appliances is an argument to a function. I do not know the type of the argument at runtime, but it appears to be a list of scalar values. I do know that when I invoke the function, I see the following error message:
File ".../database.py", line 8277, in get_report_appliance_list
req_appliances[i] = str(req_appliances[i])
TypeError: 'str' object does not support item assignment
I'm trying to deduce for what values of argument req_appliances it would be possible for this error condition to arise. It seems to me that all of the values are scalar and each (even if immutable) should be a valid LHS expressions in an assignment. Is there something I'm missing here? Here is the code in context, in the function in which it is defined:
def get_report_appliance_list(self, req_appliances, filter_type=None):
appliances = {}
appliance_list = []
if filter_type != None:
if filter_type not in ('appliances', 'servers'):
raise ValueError("appliance filter_type must be one of 'appliances' or 'servers'")
active_con = self.get_con()
if active_con is None:
raise Exception('No database connections are available.')
con = None
in_expr_items = ''
if req_appliances != None:
# Create a string like '(%s, %s, ...)' to represent
# the 'in' expression items in the SQL.
print(req_appliances)
for i in range(0, len(req_appliances)):
req_appliances[i] = str(req_appliances[i])
in_expr_items += '%s,'
in_expr_items = '(' + in_expr_items[:-1] + ') '
An str acts like a sequence type (you can iterate over it), but strings in Python are immutable, so you can't assign new values to any of the indices.
I expect what's happening here is that you're trying to run this when req_appliances is a str object.
I came up with two ways to fix this:
First, just check if it's a str before you iterate over it:
if isinstance(req_appliances, basestring):
return req_appliances
Second, you could check each item to see if it's already a string before trying to assign to it.
req_appliances = ['9087000CD', 'Olympus', 185]
for i in range(0, len(req_appliances)):
if req_appliances[i] != str(req_appliances[i]):
req_appliances[i] = str(req_appliances[i])
print req_appliances
Here, I'm actually checking whether the member is equal to its string representation. This is true when you iterate over strings.
>>> a = 'a'
>>> a[0] == str(a[0])
True
This is not really an answer to your question, but a style advise. If you happen to use for i in range(0, len(something)) a lot you should either use for i, obj in enumerate(something), map(func, something) or a list comprehension [func(x) for x in something].
Another red flag is the use of string += inside a loop. Better create an array and join it. This also eliminates the need to do stuff like [-1] in order to get rid of trailing commas.
Regarding your code you could simplify it a lot:
def get_report_appliance_list(self, req_appliances, filter_type=None):
appliances = {}
appliance_list = []
if filter_type not in (None, 'appliances', 'servers'):
raise ValueError("appliance filter_type must be one of 'appliances' or 'servers'")
active_con = self.get_con()
if active_con is None:
raise Exception('No database connections are available.')
# Create a string like '(%s, %s, ...)' to represent
# the 'in' expression items in the SQL.
in_expr_items = ','.join(['%s'] * len(req_appliances)
req_appliances = map(str, req_appliances)
...
Apart from that I would recommend that get_con() throws so you do not have to check for None in your code.

Categories