##  [Numbers and "numbers" in Python](/blog/numbers-and-numbers-python) 

# Numbers and `numbers` in Python

Did you know that Python has **three** base number types?

- integers
- floats
- complex numbers

If you're new to Python, but used to Engineering, this may come as a bit of surprise, but a welcome one. For those of you that are the reverse, complex numbers are incredibly commonly used in engineering; you can do some really cool stuff with them. In Python they look like this.

```IPython
In [1]: a = 1 - 4j

In [2]: type(a)
Out[2]: complex

In [3]: a.imag
Out[3]: -4.0

In [4]: a.real
Out[4]: 1.0

In [5]: a.conjugate()
Out[5]: (1+4j)

In [6]:

```

Pretty neat! You can also access a lot of complex number operations via the built-in library [`cmath`](https://docs.python.org/3/library/cmath.html), but that's a topic for another time.

You can actually access quite a lot of additional types of number from commonly used packages, but particularly from `numpy` specifically, which provides things like [`numpy.int64`](https://numpy.org/doc/stable/reference/arrays.scalars.html?highlight=int64#numpy.int64): a signed, 64-bit integer. Types like this are commonly built-in in older languages more oriented toward number crunching, with FORTRAN perhaps being the most obvious example (if ever there was a language made for working with numbers it was FORTRAN). However, I assume Python eschewed them in favour of accessibility. Python is about being accessible and readable, so there are just 3 base numerical types and one of those is fairly uncommon (complex numbers).

## OK, great, but when are you going to talk about `numbers`?

Well, the important part of the preamble is to establish that Python *does* have a lot of numerical types, despite appearances. Many of those are available through `numpy`, but this **is** Ansys, *the* engineering software company. Numerical computation is what we *do*. So we often end up working with them, a lot. But Python is also a dynamically typed language and one that is particularly relaxed when it comes to numbers.

```IPython
In [7]: 1 + 1.0
Out[7]: 2.0

In [8]: 2.0 + (3+5j)
Out[8]: (5+5j)

In [9]: 6/(5+8j)
Out[9]: (0.33707865168539325-0.5393258426966292j)

In [10]: 7./14
Out[10]: 0.5

In [11]: 14/7.
Out[11]: 2.0

In [12]:

```

It will happily let you change the numerical type of a variable in your program's execution, and it probably won't even cause any problems (in your code). However, this doesn't mean problems aren't caused... For example, when working with data it is not uncommon to come across mis-entered data or corrupted information, and get things like NaNs ("not a number") when attempting to parse something that's not quite right.

```IPython
In [12]: float('nan')
Out[12]: nan

In [13]:

```

&gt; NaNs are tricky beasts to work with and I won't dwell on them here, but you should check out [this post](https://towardsdatascience.com/navigating-the-hell-of-nans-in-python-71b12558895b) by Julia Di Rosso if you want to know more.

And at some point as a developer you will hit this problem.

"How do I check if my variable is a number?"

That's where [`numbers`](https://docs.python.org/3/library/numbers.html) comes in. This is a builtin-library of numeric abstract base classes (or "ABCs").

&gt; If you know what that means, great! If you don't then the quick summary is that its a collection of classes that are not intended to ever be instantiated. Instead, they are to be inherited from, with built-in methods that can be overwritten or made use of in other ways. Python has a built-in library for building your own or otherwise working with them called [`abc`](https://docs.python.org/3/library/abc.html)

In particular `numbers` has a variety of numeric ABCs but the most general one, is `Number`. This is how you solve the posed problem. We can use it in conjunction with `isinstance` to check if *anything* we pass to it is a numeric value!

```IPython
In [1]: import numpy as np

In [2]: from numbers import Number

In [3]: for i in [1, 1., -0.2, 1e8, np.int64(1), np.int0(10), np.int16(2), np.float64(10), np.complex64(10), np.int32(89)]:
   ...:     print(isinstance(i, Number))
   ...:
True
True
True
True
True
True
True
True
True
True

```

&gt; `isinstance(a, b)` is a simple built-in that checks if the first provided argument `a` is an instantiated instance of the second `b`. Generally, it should be avoided as it is considered a code "smell". &gt;&gt; Code "smells" are features of a program that *suggest* an underlying issue, but aren't necessarily bad in and of themselves. `isinstance` is a good example because the function isn't *bad* but it is commonly misused, so seeing it appear in a program could be a sign that something elsewhere is wrong. If you find yourself using it a lot you should take a closer look at your code and work out why!

## Conclusion

As explained above, you should avoid using `isinstance` in long programs generally. However, when debugging or scripting, which we, as developers, probably do more than anything else, this is an invaluable tool to have at your disposal. It's another tool for the box.