Comparisons
Although we have covered comparison operators in earlier chapters, there are a few
occasions where it is easy to get unexpected results with what may seem like fairly simple
tests. For example, you might do the following to detect an undefined value:
if not x:
# Do something if x is logically false
This will work in all situations where the variable has a value that is logically false, like
0.0, None or an empty list. However, sometimes you actually want the test to be more
specific, for example, to catch None when the variable is undefined but not catch 0.0 when
it is a useful numeric value. In this case the test should be:
if x is None:
# Does not get here if x is zero
Clearly, sometimes you do actually want the more general test ‘if not x’ to catch all
logically false values, depending on the situation.
When comparing whether two things are the same we can use either ‘==’ or ‘is’.
Remember that the double equal sign checks whether the two items have the same value,
but the is keyword checks whether they are the same Python object. This can be important
where there are two different objects but they hold the same value. For example, when
comparing integer and floating point numbers:
x = 5.0 # Floating point
y = 5 # Integer
if x == y:
# Succeeds; same values
if x is y:
# Fails; different objects
Hence when working with numbers ‘==’ is usually what we want. Conversely, when
working with more complicated Python objects it is often best to use the is keyword, so
you know you really have the same object, because classes can actually be written to yield
the same value in a comparison, even if they are not the same entity.
In many situations it is useful to act according to what the data type of an object is.
Remembering that we can get the data type of a Python object with the inbuilt type(), we
could check to see if a variable is of a specific type:
if type(x) is list:
# Do something only if x is a list
The above example relies on the fact that the keyword list represents a data type, even
though we often use it as a function, to create lists. Even if there is not a convenient
keyword, in Python 2 we can get hold of all inbuilt Python data types via the types
module, e.g.
import types
print(dir(types)) # List things in the module
In Python 3 the types module exists but does not include data types with built-in
keywords (like int or list). This module enables you to do:
if type(x) is types.FunctionType:
# Do something only if x is a function
The comparison data type could also be generated within the statement using the right
kind of object (here a list, albeit an empty one):
if type(x) is type([]):
# Do something only if x is a list
Type checking can also be done using the inbuilt isinstance() function:
if isinstance(x, list):
# Do something only if x is a list
And this is especially handy if there are several different data types that you want to
catch. For example, we may wish to do something if the variable is a list or a tuple or a
set.
if isinstance(x, (list, tuple, set)):
# Do something if x is a list, tuple or set
Alternatively, this could be achieved in a more verbose manner by combining multiple
tests with the OR operator. Using isinstance is helpful when working with object classes
and their hierarchies, especially considering that with old-style Python version 2 classes
7
using type() doesn’t help:
class Something: # Python: old style
def func(self):
print("First class")
class Different:
def otherFunc(self):
print("Second class")
a = Something()
b = Different()
type(a) is type(b) # True; not what you might expect! Python only
# These are both type 'instance'
isinstance(b, Something) # False
isinstance(a, Different) # False
As mentioned before, isinstance can also be used when one object is a subclass of
another. Taking an example from
Chapter 7
where a Protein is a subclass of Molecule:
myProtein = Protein('Enzyme A', sequence) # a pre-specified sequence
if isinstance(myProtein, Molecule):
# Succeeds : Classes are different, but Protein is a subclass of Molecule
In Python version 2 the comparison operations (such as ==, !=, <, >, <=, >= etc.) will
work with all different data types, and while we might expect to compare floating point
numbers with integers we can actually compare all types, whether it makes much sense or
not:
a = 'Banana' # A text string
b = 5 # An integer
a == 5 # False; as you might expect
a > b # True in Python 2; arbitrary, but consistent
Where a value comparison doesn’t make too much sense Python arbitrarily deems one
value to be greater than the other, including for custom classes, although it always does
this in a consistent way. This may seem odd, but such comparisons are handy in various
situations. For example, you can sort a list containing items of mixed data types and get
the same result each time. However, it can be argued that allowing meaningless
comparisons allows errors to go unnoticed. It is for perhaps this reason that in Python
version 3 comparisons cannot be made between incompatible types, so here comparing
integer with floating point works, but integer and string does not.
When dealing with comparisons and arrays created using the NumPy module, we have
to be especially careful. It might be expected that the comparison operations with a
numpy.array object give back a single True or False, as is generally the case in Python.
However, they actually give back arrays of True and/or False:
from numpy import array
a = array([1, 0, 9, 1])
b = array([2, 0, 3, 1])
print(a == b) # Gives array([False, True, False, True])
As you can see, the array comparisons are element-wise comparisons. While this kind
of result can be handy in a NumPy context it has some important consequences. For
example, unlike with lists, sets or tuples, a NumPy array comparison cannot be placed
directly in a logic test:
if a == b:
# Never gets here! Raises a ValueError
Also, although you can put such arrays in a list, the list cannot be sorted (because
sorting needs plain True or False to come from value comparison).
myList = [a, b]
myList.sort() # Fails! Raises a ValueError
Do'stlaringiz bilan baham: |