#!/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: Variables and Objects](03-Semantics-Variables.ipynb) | [Contents](Index.ipynb) | [Built-In Types: Simple Values](05-Built-in-Scalar-Types.ipynb) >
# # Basic Python Semantics: Operators
# In the previous section, we began to look at the semantics of Python variables and objects; here we'll dig into the semantics of the various *operators* included in the language.
# By the end of this section, you'll have the basic tools to begin comparing and operating on data in Python.
# ## Arithmetic Operations
# Python implements seven basic binary arithmetic operators, two of which can double as unary operators.
# They are summarized in the following table:
#
# | Operator | Name | Description |
# |--------------|----------------|--------------------------------------------------------|
# | ``a + b`` | Addition | Sum of ``a`` and ``b`` |
# | ``a - b`` | Subtraction | Difference of ``a`` and ``b`` |
# | ``a * b`` | Multiplication | Product of ``a`` and ``b`` |
# | ``a / b`` | True division | Quotient of ``a`` and ``b`` |
# | ``a // b`` | Floor division | Quotient of ``a`` and ``b``, removing fractional parts |
# | ``a % b`` | Modulus | Integer remainder after division of ``a`` by ``b`` |
# | ``a ** b`` | Exponentiation | ``a`` raised to the power of ``b`` |
# | ``-a`` | Negation | The negative of ``a`` |
# | ``+a`` | Unary plus | ``a`` unchanged (rarely used) |
#
# These operators can be used and combined in intuitive ways, using standard parentheses to group operations.
# For example:
# In[1]:
# addition, subtraction, multiplication
(4 + 8) * (6.5 - 3)
# Floor division is true division with fractional parts truncated:
# In[2]:
# True division
print(11 / 2)
# In[3]:
# Floor division
print(11 // 2)
# The floor division operator was added in Python 3; you should be aware if working in Python 2 that the standard division operator (``/``) acts like floor division for integers and like true division for floating-point numbers.
#
# Finally, I'll mention an eighth arithmetic operator that was added in Python 3.5: the ``a @ b`` operator, which is meant to indicate the *matrix product* of ``a`` and ``b``, for use in various linear algebra packages.
# ## Bitwise Operations
# In addition to the standard numerical operations, Python includes operators to perform bitwise logical operations on integers.
# These are much less commonly used than the standard arithmetic operations, but it's useful to know that they exist.
# The six bitwise operators are summarized in the following table:
#
# | Operator | Name | Description |
# |--------------|-----------------|---------------------------------------------|
# | ``a & b`` | Bitwise AND | Bits defined in both ``a`` and ``b`` |
# | a | b
| Bitwise OR | Bits defined in ``a`` or ``b`` or both |
# | ``a ^ b`` | Bitwise XOR | Bits defined in ``a`` or ``b`` but not both |
# | ``a << b`` | Bit shift left | Shift bits of ``a`` left by ``b`` units |
# | ``a >> b`` | Bit shift right | Shift bits of ``a`` right by ``b`` units |
# | ``~a`` | Bitwise NOT | Bitwise negation of ``a`` |
#
# These bitwise operators only make sense in terms of the binary representation of numbers, which you can see using the built-in ``bin`` function:
# In[4]:
bin(10)
# The result is prefixed with ``'0b'``, which indicates a binary representation.
# The rest of the digits indicate that the number 10 is expressed as the sum $1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 0 \cdot 2^0$.
# Similarly, we can write:
# In[5]:
bin(4)
# Now, using bitwise OR, we can find the number which combines the bits of 4 and 10:
# In[6]:
4 | 10
# In[7]:
bin(4 | 10)
# These bitwise operators are not as immediately useful as the standard arithmetic operators, but it's helpful to see them at least once to understand what class of operation they perform.
# In particular, users from other languages are sometimes tempted to use XOR (i.e., ``a ^ b``) when they really mean exponentiation (i.e., ``a ** b``).
# ## Assignment Operations
# We've seen that variables can be assigned with the "``=``" operator, and the values stored for later use. For example:
# In[8]:
a = 24
print(a)
# We can use these variables in expressions with any of the operators mentioned earlier.
# For example, to add 2 to ``a`` we write:
# In[9]:
a + 2
# We might want to update the variable ``a`` with this new value; in this case, we could combine the addition and the assignment and write ``a = a + 2``.
# Because this type of combined operation and assignment is so common, Python includes built-in update operators for all of the arithmetic operations:
# In[10]:
a += 2 # equivalent to a = a + 2
print(a)
# There is an augmented assignment operator corresponding to each of the binary operators listed earlier; in brief, they are:
#
# |||||
# |-|-|
# |``a += b``| ``a -= b``|``a *= b``| ``a /= b``|
# |``a //= b``| ``a %= b``|``a **= b``|``a &= b``|
# |a |= b
| ``a ^= b``|``a <<= b``| ``a >>= b``|
#
# Each one is equivalent to the corresponding operation followed by assignment: that is, for any operator "``■``", the expression ``a ■= b`` is equivalent to ``a = a ■ b``, with a slight catch.
# For mutable objects like lists, arrays, or DataFrames, these augmented assignment operations are actually subtly different than their more verbose counterparts: they modify the contents of the original object rather than creating a new object to store the result.
# ## Comparison Operations
#
# Another type of operation which can be very useful is comparison of different values.
# For this, Python implements standard comparison operators, which return Boolean values ``True`` and ``False``.
# The comparison operations are listed in the following table:
#
# | Operation | Description || Operation | Description |
# |---------------|-----------------------------------||---------------|--------------------------------------|
# | ``a == b`` | ``a`` equal to ``b`` || ``a != b`` | ``a`` not equal to ``b`` |
# | ``a < b`` | ``a`` less than ``b`` || ``a > b`` | ``a`` greater than ``b`` |
# | ``a <= b`` | ``a`` less than or equal to ``b`` || ``a >= b`` | ``a`` greater than or equal to ``b`` |
#
# These comparison operators can be combined with the arithmetic and bitwise operators to express a virtually limitless range of tests for the numbers.
# For example, we can check if a number is odd by checking that the modulus with 2 returns 1:
# In[11]:
# 25 is odd
25 % 2 == 1
# In[12]:
# 66 is odd
66 % 2 == 1
# We can string-together multiple comparisons to check more complicated relationships:
# In[13]:
# check if a is between 15 and 30
a = 25
15 < a < 30
# And, just to make your head hurt a bit, take a look at this comparison:
# In[14]:
-1 == ~0
# Recall that ``~`` is the bit-flip operator, and evidently when you flip all the bits of zero you end up with -1.
# If you're curious as to why this is, look up the *two's complement* integer encoding scheme, which is what Python uses to encode signed integers, and think about what happens when you start flipping all the bits of integers encoded this way.
# ## Boolean Operations
# When working with Boolean values, Python provides operators to combine the values using the standard concepts of "and", "or", and "not".
# Predictably, these operators are expressed using the words ``and``, ``or``, and ``not``:
# In[15]:
x = 4
(x < 6) and (x > 2)
# In[16]:
(x > 10) or (x % 2 == 0)
# In[17]:
not (x < 6)
# Boolean algebra aficionados might notice that the XOR operator is not included; this can of course be constructed in several ways from a compound statement of the other operators.
# Otherwise, a clever trick you can use for XOR of Boolean values is the following:
# In[18]:
# (x > 1) xor (x < 10)
(x > 1) != (x < 10)
# These sorts of Boolean operations will become extremely useful when we begin discussing *control flow statements* such as conditionals and loops.
#
# One sometimes confusing thing about the language is when to use Boolean operators (``and``, ``or``, ``not``), and when to use bitwise operations (``&``, ``|``, ``~``).
# The answer lies in their names: Boolean operators should be used when you want to compute *Boolean values (i.e., truth or falsehood) of entire statements*.
# Bitwise operations should be used when you want to *operate on individual bits or components of the objects in question*.
# ## Identity and Membership Operators
#
# Like ``and``, ``or``, and ``not``, Python also contains prose-like operators to check for identity and membership.
# They are the following:
#
# | Operator | Description |
# |---------------|---------------------------------------------------|
# | ``a is b`` | True if ``a`` and ``b`` are identical objects |
# | ``a is not b``| True if ``a`` and ``b`` are not identical objects |
# | ``a in b`` | True if ``a`` is a member of ``b`` |
# | ``a not in b``| True if ``a`` is not a member of ``b`` |
# ### Identity Operators: "``is``" and "``is not``"
#
# The identity operators, "``is``" and "``is not``" check for *object identity*.
# Object identity is different than equality, as we can see here:
# In[19]:
a = [1, 2, 3]
b = [1, 2, 3]
# In[20]:
a == b
# In[21]:
a is b
# In[22]:
a is not b
# What do identical objects look like? Here is an example:
# In[23]:
a = [1, 2, 3]
b = a
a is b
# The difference between the two cases here is that in the first, ``a`` and ``b`` point to *different objects*, while in the second they point to the *same object*.
# As we saw in the previous section, Python variables are pointers. The "``is``" operator checks whether the two variables are pointing to the same container (object), rather than referring to what the container contains.
# With this in mind, in most cases that a beginner is tempted to use "``is``" what they really mean is ``==``.
# ### Membership operators
# Membership operators check for membership within compound objects.
# So, for example, we can write:
# In[24]:
1 in [1, 2, 3]
# In[25]:
2 not in [1, 2, 3]
# These membership operations are an example of what makes Python so easy to use compared to lower-level languages such as C.
# In C, membership would generally be determined by manually constructing a loop over the list and checking for equality of each value.
# In Python, you just type what you want to know, in a manner reminiscent of straightforward English prose.
#
# < [Basic Python Semantics: Variables and Objects](03-Semantics-Variables.ipynb) | [Contents](Index.ipynb) | [Built-In Types: Simple Values](05-Built-in-Scalar-Types.ipynb) >