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.
Related
I am running in this multiple times now and never found a satisfying solution.
I want to check: Is variable some_var of a number type?
More specific for me: Can it be compared to another number? (e.g. a string should fail)
Numpy makes this somehow difficult since:
some_var = np.int32(123)
isinstance(some_var, int)
>>> False
Possible Solutions
numpy solutions: np.issubtype, comparison to np.number, np.isscalar
I found np.issubtype as workaround, but this will fail for non-numpy types:
some_var = 123
np.issubdtype(some_var.dtype, np.number)
>>> AttributeError: 'int' object has no attribute 'dtype'
np.number can be used for comparison, but also fails for non-numpy types
isinstance(1, np.number)
>>> False
np.isscalar() works fine, but also allows strings:
np.isscalar('test')
>>>True
numbers module
There is the comparison with the numbers module. Which seems to work convenient, but needs an extra import. So far probably the best solution.
from numbers import Integral
isinstance(some_var, Integral)
>>> True
Manual testing
The clearest way of course would be a manual test.
isinstance(some_var, (int, float, np.int32, np.int64, np.float32, np.float64))
>>> True
My Questions
Am I missing a way?
What would be the most recommended?
As the short answer did not catch the audience, here the extended answer:
There is no generic function to check against every numeric type in existance, as every package can implement their own.
It is also advisable, to control, which variable types to accept, as packages can have different implementation behaviour (e.g. integer division 1 / 10 = 0.1 or 1 / 10 = 0.
Therefore a simple isNumeric check is advisable.
Now two versions come to my mind:
NUMERIC_TYPES = [int, float, ...] #to be extended by acceptable types
def isNumeric(val):
return isinstance(val, NUMERIC_TYPES)
however there is a more performant version (my laptop 15%)
NUMERIC_TYPES = set((int, float, np.int32, np.int64, np.float32, np.float64,...))
def isNumeric(val):
return type(val) in NUMERIC_TYPES
And for #CodePrinz isinstance and type(x)== is not the same!
class StupidInt(int):
def __init__(self, val):
self._privateInt = val
def __add__(self, other):
return self._privateInt - other
a = StupidInt(10)
b = StupidInt(10)
print("a + b = 0? whaaat?")
print(a+b)
print("isInstance: {}".format(isinstance(a, int)))
print("is of type: {}".format(type(a) == int))
----
a + b = 0? whaaat?
0
isInstance: True
is of type: False
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
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.
I found some old Python code that was doing something like:
if type(var) is type(1):
...
As expected, pep8 complains about this recommending usage of isinstance().
Now, the problem is that the numbers module was added in Python 2.6 and I need to write code that works with Python 2.5+
So if isinstance(var, Numbers.number) is not a solution.
Which would be the proper solution in this case?
In Python 2, you can use the types module:
>>> import types
>>> var = 1
>>> NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)
>>> isinstance(var, NumberTypes)
True
Note the use of a tuple to test against multiple types.
Under the hood, IntType is just an alias for int, etc.:
>>> isinstance(var, (int, long, float, complex))
True
The complex type requires that your python was compiled with support for complex numbers; if you want to guard for this use a try/except block:
>>> try:
... NumberTypes = (types.IntType, types.LongType, types.FloatType, types.ComplexType)
... except AttributeError:
... # No support for complex numbers compiled
... NumberTypes = (types.IntType, types.LongType, types.FloatType)
...
or if you just use the types directly:
>>> try:
... NumberTypes = (int, long, float, complex)
... except NameError:
... # No support for complex numbers compiled
... NumberTypes = (int, long, float)
...
In Python 3 types no longer has any standard type aliases, complex is always enabled and there is no longer a long vs int difference, so in Python 3 always use:
NumberTypes = (int, float, complex)
Last but not least, you can use the numbers.Numbers abstract base type (new in Python 2.6) to also support custom numeric types that don't derive directly from the above types:
>>> import numbers
>>> isinstance(var, numbers.Number)
True
This check also returns True for decimal.Decimal() and fractions.Fraction() objects.
This module does make the assumption that the complex type is enabled; you'll get an import error if it is not.
Python 2 supports four types for numbers int,float, long and complexand python 3.x supports 3:int, float and complex
>>> num = 10
>>> if isinstance(num, (int, float, long, complex)): #use tuple if checking against multiple types
print('yes it is a number')
yes it is a number
>>> isinstance(num, float)
False
>>> isinstance(num, int)
True
>>> a = complex(1, 2)
>>> isinstance(a, complex)
True
Depending on what you're using this in duck typing could be a better approach (it's certainly commonly recommended). The problem with Martijn Pieters' approach is that you will always miss some types of number from your list. Off the top of my head your code won't work with: sympy rational numbers, arbitrary precision integers and any implementation of complex numbers.
One alternative is to write a function like this:
def is_number(thing):
try:
thing + 1
return True
except TypeError:
return False
This code should work with any reasonable implementation of a number. Of course there is a major downside: it will also work with an unreasonable implementation of plenty of non-numbers (i.e. if the plus operator is overloaded and accepts an integer).
Another alternative (depending on why you need to know if something is a number) is to just assume it is a number, and if it isn't errors will be thrown by whichever bit of the code requires a number.
I'm not saying these approaches are always better (unlike some people...) just that they are worth considering.
One can use the numbers module from the The Python Standard Library:
# import numbers
isinstance(var, numbers.Number)
tells if var is a number. Examples:
import numbers
var = 5 ; print(isinstance(var, numbers.Number)) # True
var = 5.5; print(isinstance(var, numbers.Number)) # True
var = 'a'; print(isinstance(var, numbers.Number)) # False
import numpy as np
var = np.float128(888); print(isinstance(var, numbers.Number)) # True
class C: pass; var = C(); print(isinstance(c, numbers.Number)) # False
import imaplib, re
import os
while(True):
conn = imaplib.IMAP4_SSL("imap.gmail.com", 993)
conn.login("xxx", "xxxx")
unreadCount = re.search("UNSEEN (\d+)", conn.status("INBOX", "(UNSEEN)")[1][0]).group(1)
print unreadCount
if unreadCount > 10:
os.system('ls')
Even when unreadCount is < 10, it runs the command 'ls'. Why?
You might want to coerce that value to an integer, as per:
unreadCount = int (re.search (blah, blah, blah).group (1))
The call to re.search is returning a string and, if you have a look at the following transcript:
>>> x = "7"
>>> if x > 10:
... print "yes"
...
yes
>>> if int(x) > 10:
... print "yes"
...
>>> x = 7
>>> if x > 10:
... print "yes"
...
>>>
you'll see why that's not such a good idea.
The reason you're seeing this (what you might call bizarre) behaviour can be gleaned from the manual at the bottom of 5.3:
CPython implementation detail: Objects of different types except numbers are ordered by their type names; objects of the same types that don’t support proper comparison are ordered by their address.
Since the type of "7" is str and the type of 10 is int, it's simply comparing the type names ("str" is always greater than "int" in an alpha order), leading to some interesting things like:
>>> "1" > 99999999999999999999999
True
>>> "1" == 1
False
That implementation detail was still in effect up until at least 2.7.2. It may have changed in the Python 3000 stream (the clause has certainly been removed from the relevant documentation section), but the documentation there still states:
Most other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.
So it's probably not something you should rely on.
Try this:
if int(unreadCount) > 10:
os.system('ls')
You're comparing a string to an integer:
>>> '10' > 10
True
This may be shocking; whether the string is coerced to an integer or the integer is coerced to a string, in both cases the result should have been False. The truth is that neither happens, and the ordering is arbitrary. From the language reference:
Most other objects of built-in types compare unequal unless they are the same object; the choice whether one object is considered smaller or larger than another one is made arbitrarily but consistently within one execution of a program.
This will solve your problem:
unreadCount = int(re.search(...).group(1))