This notebook contains an excerpt from the Whirlwind Tour of Python by Jake VanderPlas; the content is available on GitHub.
The text and code are released under the CC0 license; see also the companion project, the Python Data Science Handbook.
05: Built-In Types: Simple Values#
When discussing Python variables and objects, we mentioned the fact that all Python objects have type information attached. Here we’ll briefly walk through the built-in simple types offered by Python. We say “simple types” to contrast with several compound types, which will be discussed in the following section.
Klein 2021: Python 3
4.3 Datentypen
Python’s simple types are summarized in the following table:
Python Scalar Types
Type |
Example |
Description |
---|---|---|
|
|
integers (i.e., whole numbers) |
|
|
floating-point numbers (i.e., real numbers) |
|
|
Complex numbers (i.e., numbers with real and imaginary part) |
|
|
Boolean: True/False values |
|
|
String: characters or text |
|
|
Special object indicating nulls |
We’ll take a quick look at each of these in turn.
Integers#
The most basic numerical type is the integer. Any number without a decimal point is an integer:
x = 1
type(x)
int
Python integers are actually quite a bit more sophisticated than integers in languages like C
.
C integers are fixed-precision, and usually overflow at some value (often near \(2^{31}\) or \(2^{63}\), depending on your system).
Python integers are variable-precision, so you can do computations that would overflow in other languages:
2 ** 200
1606938044258990275541962092341162602522202993782792835301376
Another convenient feature of Python integers is that by default, division up-casts to floating-point type:
5 / 2
2.5
Note that this upcasting is a feature of Python 3; in Python 2, like in many statically-typed languages such as C, integer division truncates any decimal and always returns an integer:
# Python 2 behavior
>>> 5 / 2
2
To recover this behavior in Python 3, you can use the floor-division operator:
5 // 2
2
Finally, note that although Python 2.x had both an int
and long
type, Python 3 combines the behavior of these two into a single int
type.
Floating-Point Numbers#
The floating-point type can store fractional numbers. They can be defined either in standard decimal notation, or in exponential notation:
x = 0.000005
y = 5e-6
print(x == y)
True
x = 1400000.00
y = 1.4e6
print(x == y)
True
In the exponential notation, the e
or E
can be read “…times ten to the…”,
so that 1.4e6
is interpreted as \(~1.4 \times 10^6\).
An integer can be explicitly converted to a float with the float
constructor:
float(1)
1.0
Aside: Floating-point precision#
One thing to be aware of with floating point arithmetic is that its precision is limited, which can cause equality tests to be unstable. For example:
0.1 + 0.2 == 0.3
False
Why is this the case? It turns out that it is not a behavior unique to Python, but is due to the fixed-precision format of the binary floating-point storage used by most, if not all, scientific computing platforms. All programming languages using floating-point numbers store them in a fixed number of bits, and this leads some numbers to be represented only approximately. We can see this by printing the three values to high precision:
print("0.1 = {0:.17f}".format(0.1))
print("0.2 = {0:.17f}".format(0.2))
print("0.3 = {0:.17f}".format(0.3))
0.1 = 0.10000000000000001
0.2 = 0.20000000000000001
0.3 = 0.29999999999999999
We’re accustomed to thinking of numbers in decimal (base-10) notation, so that each fraction must be expressed as a sum of powers of 10:
In the familiar base-10 representation, we represent this in the familiar decimal expression: \(0.125\).
Computers usually store values in binary notation, so that each number is expressed as a sum of powers of 2:
In a base-2 representation, we can write this \(0.001_2\), where the subscript 2 indicates binary notation. The value \(0.125 = 0.001_2\) happens to be one number which both binary and decimal notation can represent in a finite number of digits.
In the familiar base-10 representation of numbers, you are probably familiar with numbers that can’t be expressed in a finite number of digits. For example, dividing \(1\) by \(3\) gives, in standard decimal notation:
The 3s go on forever: that is, to truly represent this quotient, the number of required digits is infinite!
Similarly, there are numbers for which binary representations require an infinite number of digits. For example:
Just as decimal notation requires an infinite number of digits to perfectly represent \(1/3\), binary notation requires an infinite number of digits to represent \(1/10\). Python internally truncates these representations at 52 bits beyond the first nonzero bit on most systems.
This rounding error for floating-point values is a necessary evil of working with floating-point numbers. The best way to deal with it is to always keep in mind that floating-point arithmetic is approximate, and never rely on exact equality tests with floating-point values.
Complex Numbers#
Complex numbers are numbers with real and imaginary (floating-point) parts. We’ve seen integers and real numbers before; we can use these to construct a complex number:
complex(1, 2)
(1+2j)
Alternatively, we can use the “j
” suffix in expressions to indicate the imaginary part:
1 + 2j
(1+2j)
Complex numbers have a variety of interesting attributes and methods, which we’ll briefly demonstrate here:
c = 3 + 4j
c.real # real part
3.0
c.imag # imaginary part
4.0
c.conjugate() # complex conjugate
(3-4j)
abs(c) # magnitude, i.e. sqrt(c.real ** 2 + c.imag ** 2)
5.0
String Type#
Strings in Python are created with single or double quotes:
message = "what do you like?"
response = 'spam'
Python has many extremely useful string functions and methods; here are a few of them:
# length of string
len(response)
4
# Make upper-case. See also str.lower()
response.upper()
'SPAM'
# Capitalize. See also str.title()
message.capitalize()
'What do you like?'
# concatenation with +
message + response
'what do you like?spam'
# multiplication is multiple concatenation
5 * response
'spamspamspamspamspam'
# Access individual characters (zero-based indexing)
message[0]
'w'
For more discussion of indexing in Python, see “Lists”.
None Type#
Python includes a special type, the NoneType
, which has only a single possible value: None
. For example:
type(None)
NoneType
You’ll see None
used in many places, but perhaps most commonly it is used as the default return value of a function.
For example, the print()
function in Python 3 does not return anything, but we can still catch its value:
return_value = print('abc')
abc
print(return_value)
None
Likewise, any function in Python with no return value is, in reality, returning None
.
Boolean Type#
The Boolean type is a simple type with two possible values: True
and False
, and is returned by comparison operators discussed previously:
result = (4 < 5)
result
True
type(result)
bool
Keep in mind that the Boolean values are case-sensitive: unlike some other languages, True
and False
must be capitalized!
print(True, False)
True False
Booleans can also be constructed using the bool()
object constructor: values of any other type can be converted to Boolean via predictable rules.
For example, any numeric type is False if equal to zero, and True otherwise:
bool(2014)
True
bool(0)
False
bool(3.1415)
True
The Boolean conversion of None
is always False:
bool(None)
False
For strings, bool(s)
is False for empty strings and True otherwise:
bool("")
False
bool("abc")
True
For sequences, which we’ll see in the next section, the Boolean representation is False for empty sequences and True for any other sequences
bool([1, 2, 3])
True
bool([])
False