try:
return float(x)
except:
return 0
So this function uses the try except statement but is there an alternative way of doing this??
Your code looks like it would work but I would always make sure to define the exception you are trying to catch. In this case ValueError
Another way to write this would be
def func(x):
try:
y = float(x)
except ValueError:
return 0
# do some more with y if it was changed to a float
return y
Assuming x is a str, you could use regex to see if that str is formatted properly to be cast as a float.
import re
float_regex = re.compile(r"^-?(\d*\.)?\d+$")
if float_regex.match(x):
return float(x)
return 0
The ^ ... $ at beginning and end ensure that the match isn't a substring and that the full string can only contain the inner regex. The string must start and stop exactly as defined.
-? allows an optional minus sign
(\d*\.)? optionally allows a digit followed by a decimal
\d+ requires at least 1 digit
See here for more details on the regex expression used: https://regex101.com/r/3RXmM1/1
All of this being said, use your original code, except make sure you only catch the ValueError exception. Using regex is overkill here unless you have a specific reason. If python can't cast as a float then just let it do the work for you.
Try/except seems like to simplest and best solution, but you could use an if statement similar to this:
if str(x).lstrip('-').replace('.', '', 1).isdigit():
return float(x)
return 0
The if statement will remove - if it's a negative number and exactly one . from the input and then check there is only digits left. It converts it to a string first so we can use .replace().
There are still possibilities of exceptions like mentioned in the comments if x == '²'
As #jornsharpe suggested in the comments
from contextlib import suppress
x = "abc"
res= None
with suppress(ValueError):
res = float(x)
if not isinstance(res, float):
res = 0
print(res) # 0
But still not a good thind to suppress the Exceptions.
Your code is fine; using try/catch is promoted among Python coders.
Just as an exercise, I thought I'd follow the definitions in the documentation for the float function and the lexical analysis section on floating point literals. From those we can derive the following regular expression:
import re
reFloat = re.compile(r"(?i)^\s*[+-]?(((\d(_?\d)*)?\.\d(_?\d)*|\d(_?\d)*\.?)(e[+-]?\d(_?\d)*)?|nan|inf(inity)?)\s*$")
We can then define this function:
safeFloat = lambda x, default=0: float(x) if reFloat.match(x) else default
I believe this function will call float if, and only when, float(x) would not raise an exception, provided x is a string.
Some tests:
good = ["-3.14", "10.", ".001", "+1e100", "3.14e-10", "0e0", " 3.14_15_93 ", "+NaN", " -Infinity\n\f\r\t "]
for x in good:
print(x.strip(), safeFloat(x))
bad = [".", "_345", "123_", "e13", "123e", "- 1", "5-3", "1e3.4", "Infinit"]
for x in bad:
print(x.strip(), safeFloat(x)) # Always 0
Related
Consider the following simple code:
import re
def my_match(s):
if re.match("^[a-zA-Z]+", s):
return True
else:
return False
Is there a way to collapse this in a single return statement? In C we could do for example:
return match("^[a-zA-Z]+", s) ? true : false;
Is there something similar in python?
Python also supports this, although the syntaxes is a little different than most languages.
import re
def my_match(s):
return True if re.match("^[a-zA-Z]+", s) else False
In general, the Python syntax is val_when_true if cond else val_when_false, compared to the cond ? val_when_true : val_when_false you see in other languages.
In your particular case though, you can just write:
import re
def my_match(s):
return bool(re.match("^[a-zA-Z]+", s))
A more generell solution would be to use the following code line. It excludes a fit with length 0 as it specificly checks for the None statement. In this case an empty string is impossible but it is more explicit.
return re.match("^[a-zA-Z]+", s) is not None
re.match() returns a value that can be evaluated for truthiness. So if you don't need to return the exact values True and False, you can just directly return the result of the match() function:
def my_match(s):
return re.match("^[a-zA-Z]+", s)
And then the caller can say:
if my_match(x):
...
else:
...
Although in this specific case, my_match() becomes a mostly useless wrapper function, and you could just call re.match(...) directly.
if re.match(...):
...
else:
...
The other answers show the ternary equivalent in Python. But since Python also assigns truthiness to values and expressions, you could simply use:
my_match = lambda s : bool(re.match("^[a-zA-Z]+", s))
I have a function which could get a String formatted like this:
"true"^^<http://www.w3.org/2001/XMLSchema#boolean>
"100"^^<http://www.w3.org/2001/XMLSchema#int>
Now i want to split the String on the ^^ Characters and convert the first part of the string based on the second part. I also want to remove the " first before converting.
This is my code which i use for this:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
if toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>":
return bool(toReturn[0].replace('"', ""))
elif toReturn[1] == "<http://www.w3.org/2001/XMLSchema#int>":
return int(toReturn[0].replace('"', ""))
return None
But i'm not so happy with it. Is there maybe a more elegant (pythonic) way to archive this?
You can use a regex, to
check if the given value is valid
retrieve the value to cast, and the way to cast
PATTERN = re.compile(r'"(.*)"\^\^<http:.*#(\w+)>')
types = {"boolean": bool, "int": int}
def getValue(value):
m = PATTERN.fullmatch(value)
return types[m.group(2)](m.group(1)) if m else None
Instead of if len(...) you could just try to unpack the result and except a ValueError. Then you can use a dict for the types and str.strip instead of str.replace:
types = {'boolean': bool, 'int': int}
try:
value, type_hint = tObject.split('^^')
except ValueError:
return None
else:
return types[type_hint.rstrip('>').rsplit('#', 1)[1]](value.strip('"'))
Firstly, you could remove return None, since the function returns None by default.
Secondly, you could use toReturn[1].endswith("boolean>") to match the end of the string, instead of matching the whole string with toReturn[1] == "<http://www.w3.org/2001/XMLSchema#boolean>". Same with the int string as well.
Thirdly, you could store the return value in one variable before the if..elif, then you don't have to calculate it twice for each condition.
Code:
def getValue(tObject):
toReturn = tObject.split("^^")
if len(toReturn) == 2:
return_value = toReturn[0].replace('"', "")
if toReturn[1].endswith("boolean>"):
return bool(return_value)
elif toReturn[1].endswith("int>"):
return int(return_value)
This might not be much of a logic improvement, but the code does look less cluttered now. If you wan't more terse, "pythonic" ways of doing this problem, the other answers might be more suitable.
I want to code a unit converter and I need to extract the given value from the unit in the input string.
To provide a user friendly experience while using the converter I want the user to be able to input the value and the unit in the same string. My problem is that I want to extract the numbers and the letters so that I can tell the program the unit and the value and store them in two different variables. For extracting the letters, I used the in operator, and that works properly. I also found a solution for getting the numbers from the input, but that doesn't work for values with exponents.
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
b = float(a.split()[0])
Storing simple inputs like 567 mm as a float in b works but I want to be able to extract inputs like 5*10**6 mm but it says
could not convert string to float: '5*10**6'.
So what can I use to extract more complex numbers like this into a float?
Traditionally, in Python, as in many other languages, exponents are prefixed by the letter e or E. While 5 * 10**6 is not a valid floating point literal, 5e6 most definitely is.
This is something to keep in mind for the future, but it won't solve your issue with the in operator. The problem is that in can only check if something you already know is there. What if your input was 5e-8 km instead?
You should start by coming up with an unambiguously clear definition of how you identify the boundary between number and units in a string. For example, units could be the last contiguous bit of non-digit characters in your string.
You could then split the string using regular expressions. Since the first part can be an arbitrary expression, so you can evaluate it with something as simple as ast.literal_eval. The more complicated your expression can be, the more complicated your parser will have to be as well.
Here's an example to get you started:
from ast import literal_eval
import re
pattern = re.compile(r'(.*[\d\.])\s*(\D+)')
data = '5 * 10**6 mm'
match = pattern.fullmatch(data)
if not match:
raise ValueError('Invalid Expression')
num, units = match.groups()
num = literal_eval(num)
It seems that you are looking for the eval function, as noted in #Rasgel's answer. Documentation here
As some people have pointed out, it poses a big security risk.
To circumvent this, I can think of 2 ways:
1. Combine eval with regex
If you only want to do basic arithmetic operations like addition, subtraction and maybe 2**4 or sth like that, then you can use regex to first remove any non-numerical, non-arithmetic operational characters.
import re
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
# After parsing the units,
# Remove anything other than digits, +, -, *, /, . (floats), ! (factorial?) and ()
# If you require any other symbols, add them in
pruned_a = re.sub(r'[^0-9\*\+\-\/\!\.\(\)]', "", a)
result = eval(pruned_a)
2. Make sure eval doesn't actually evaluate any of your local or global variables in your python code.
result = eval(expression, {'__builtins__': None}, {})
(the above code is from another Stackoverflow answer here: Math Expression Evaluation -- there might be other solutions there that you might be interested in)
Combined
import re
a = str(input("Type in your wavelength: "))
if "mm" in a:
print("Unit = Millimeter")
# After parsing the units,
# Remove anything other than digits, +, -, *, /, . (floats), ! (factorial?) and ()
# If you require any other symbols, add them in
pruned_a = re.sub(r'[^0-9\*\+\-\/\!\.\(\)]', "", a)
result = eval(pruned_a, {'__builtins__': None}, {}) #to be extra safe :)
There are many ways to tackle this simple problem, using str.split, regular expressions, eval, ast.literal_eval... Here I propose you to have your own safe routine that will evaluate simple mathematical expressions, code below:
import re
import ast
import operator
def safe_eval(s):
bin_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.itruediv,
ast.Mod: operator.mod,
ast.Pow: operator.pow
}
node = ast.parse(s, mode='eval')
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return bin_ops[type(node.op)](_eval(node.left), _eval(node.right))
else:
raise Exception('Unsupported type {}'.format(node))
return _eval(node.body)
if __name__ == '__main__':
text = str(input("Type in your wavelength: "))
tokens = [v.strip() for v in text.split()]
if len(tokens) < 2:
raise Exception("expected input: <wavelength expression> <unit>")
wavelength = safe_eval("".join(tokens[:-1]))
dtype = tokens[-1]
print(f"You've typed {wavelength} in {dtype}")
I'll also recommend you read this post Why is using 'eval' a bad practice?
In case you have a string like 5*106and want to convert this number into a float, you can use the eval() function.
>>> float(eval('5*106'))
530.0
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) ) ):
I'm trying to make a very basic calculator to familiarize myself with the basics of python. Part of the code involves asking for inputs and setting those as different variables, but the variables put in as inputs are stored as strings, even though they're entered as numbers:
def change_x_a():
velocity_i = input("Initial Velocity?")
velocity_f = input("Final Velocity?")
time = input("Time?")
float(velocity_i)
float(velocity_f)
float(time)
answer = (0.5*(velocity_i+velocity_f)*time)
print(answer)
Is there a fix for this?
float() doesn't modify the variable you pass it. Instead, it converts the value you give it and returns a float.
So
float(velocity_i)
by itself does nothing, where
velocity_i = float(velocity_i)
will give the behavior you're looking for.
Keep in mind that float() (and the other type-conversion functions) will throw an exception if you pass them something they're not expecting. For a better user experience, you should handle these exceptions1. Typically, one does this in a loop:
while True:
try:
velocity_i = float(input("Initial Velocity?"))
break # Valid input - stop asking
except ValueError:
pass # Ignore the exception, and ask again
We can wrap this behavior up into a nice little function to make it more re-usable:
def get_input(prompt, exptype):
while True:
try:
return exptype( input(prompt) )
except ValueError:
pass # Ignore the exception, and ask again
and call it like this:
val_f = get_input('Give me a floating-point value:', float)
val_i = get_input('Give me an integer value:', int)
1 - Wow, I just realized that I independently wrote almost the exact same code as the Python tutorial, which I linked to, after the fact.
You can convert the inputs to float when you take them from the user.
Try
velocity_i = float(input("Initial Velocity?")
And so on.
Yes. Simply convert it to a float:
velocity_i = float(input("Initial Velocity?"))
or an integer:
velocity_f = int(input("Final velocity?"))