#!/usr/bin/env python
# coding: utf-8
#
#
# *This notebook contains an excerpt from the [Whirlwind Tour of Python](http://www.oreilly.com/programming/free/a-whirlwind-tour-of-python.csp) by Jake VanderPlas; the content is available [on GitHub](https://github.com/jakevdp/WhirlwindTourOfPython).*
#
# *The text and code are released under the [CC0](https://github.com/jakevdp/WhirlwindTourOfPython/blob/master/LICENSE) license; see also the companion project, the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook).*
#
#
# < [Basic Python Semantics: Operators](04-Semantics-Operators.ipynb) | [Contents](Index.ipynb) | [Built-In Data Structures](06-Built-in-Data-Structures.ipynb) >
# # 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.
#
# Python's simple types are summarized in the following table:
#
#
**Python Scalar Types**
#
# | Type | Example | Description |
# |-------------|----------------|--------------------------------------------------------------|
# | ``int`` | ``x = 1`` | integers (i.e., whole numbers) |
# | ``float`` | ``x = 1.0`` | floating-point numbers (i.e., real numbers) |
# | ``complex`` | ``x = 1 + 2j`` | Complex numbers (i.e., numbers with real and imaginary part) |
# | ``bool`` | ``x = True`` | Boolean: True/False values |
# | ``str`` | ``x = 'abc'`` | String: characters or text |
# | ``NoneType``| ``x = None`` | 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:
# In[1]:
x = 1
type(x)
# 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:
# In[2]:
2 ** 200
# Another convenient feature of Python integers is that by default, division up-casts to floating-point type:
# In[3]:
5 / 2
# 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
# # Python 2 behavior
# >>> 5 / 2
# 2
# ```
# To recover this behavior in Python 3, you can use the floor-division operator:
# In[4]:
5 // 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:
# In[5]:
x = 0.000005
y = 5e-6
print(x == y)
# In[6]:
x = 1400000.00
y = 1.4e6
print(x == y)
# 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:
# In[7]:
float(1)
# ### 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:
# In[8]:
0.1 + 0.2 == 0.3
# 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:
# In[9]:
print("0.1 = {0:.17f}".format(0.1))
print("0.2 = {0:.17f}".format(0.2))
print("0.3 = {0:.17f}".format(0.3))
# 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:
# $$
# 1 /8 = 1\cdot 10^{-1} + 2\cdot 10^{-2} + 5\cdot 10^{-3}
# $$
# 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:
# $$
# 1/8 = 0\cdot 2^{-1} + 0\cdot 2^{-2} + 1\cdot 2^{-3}
# $$
# 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:
# $$
# 1 / 3 = 0.333333333\cdots
# $$
# 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:
# $$
# 1 / 10 = 0.00011001100110011\cdots_2
# $$
# 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:
# In[10]:
complex(1, 2)
# Alternatively, we can use the "``j``" suffix in expressions to indicate the imaginary part:
# In[11]:
1 + 2j
# Complex numbers have a variety of interesting attributes and methods, which we'll briefly demonstrate here:
# In[12]:
c = 3 + 4j
# In[13]:
c.real # real part
# In[14]:
c.imag # imaginary part
# In[15]:
c.conjugate() # complex conjugate
# In[16]:
abs(c) # magnitude, i.e. sqrt(c.real ** 2 + c.imag ** 2)
# ## String Type
# Strings in Python are created with single or double quotes:
# In[17]:
message = "what do you like?"
response = 'spam'
# Python has many extremely useful string functions and methods; here are a few of them:
# In[18]:
# length of string
len(response)
# In[19]:
# Make upper-case. See also str.lower()
response.upper()
# In[20]:
# Capitalize. See also str.title()
message.capitalize()
# In[21]:
# concatenation with +
message + response
# In[22]:
# multiplication is multiple concatenation
5 * response
# In[23]:
# Access individual characters (zero-based indexing)
message[0]
# For more discussion of indexing in Python, see ["Lists"](06-Built-in-Data-Structures.ipynb#Lists).
# ## None Type
# Python includes a special type, the ``NoneType``, which has only a single possible value: ``None``. For example:
# In[24]:
type(None)
# 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:
# In[25]:
return_value = print('abc')
# In[26]:
print(return_value)
# 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:
# In[27]:
result = (4 < 5)
result
# In[28]:
type(result)
# Keep in mind that the Boolean values are case-sensitive: unlike some other languages, ``True`` and ``False`` must be capitalized!
# In[29]:
print(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:
# In[30]:
bool(2014)
# In[31]:
bool(0)
# In[32]:
bool(3.1415)
# The Boolean conversion of ``None`` is always False:
# In[33]:
bool(None)
# For strings, ``bool(s)`` is False for empty strings and True otherwise:
# In[34]:
bool("")
# In[35]:
bool("abc")
# For sequences, which we'll see in the next section, the Boolean representation is False for empty sequences and True for any other sequences
# In[36]:
bool([1, 2, 3])
# In[37]:
bool([])
#
# < [Basic Python Semantics: Operators](04-Semantics-Operators.ipynb) | [Contents](Index.ipynb) | [Built-In Data Structures](06-Built-in-Data-Structures.ipynb) >