Click Here to download this tutorial as an iPython Notebook.
Normally, each line in Python code forms a single instructional statement.
Using number literals and the built-in operators +
(add) and -
(subtract),
one can construct a simple instruction:
In [1]:
# This is a line comment.
5 + 2
6 - 3 # This is an inline comment.
3
Notice there are no line terminators like as in other languages (C, for
instance, which ends each instructional statement with a ;
). Comments are
denoted by whatever follows a #
character and can occur anywhere on a line.
Comments are invisible to the Python interpereter. There are no block comments in
Python. This is due to one of the language’s core philosophies:
“Explicit is better than implicit”.
By forcing every comment line to start with #
, the developer must make a
concious choice to comment out multiple lines. Multiple instructions can be
placed on the same line if separated by the ;
operator as in:
In [2]:
# Now both instructions are on the same line.
5 + 2; 6 - 3
3
The line above will perform both instructions sequentially. Code run in an
interactive shell will only print the result of the last instruction processed,
but both operations are in fact being executed. We can verify this by forcing
each operation to print to stdout
using the built-in print
function
(formerly a keyword prior to Python v3), which sends it’s argument to the
standard output. Function arguments are enclosed by parentheses.
In [3]:
print(5 + 2); print(6 - 3)
7
3
Like most programming languages, one can express numbers in a variety of ways
using number literals. Number literals can be of three types: either an
integer, a float, or a complex. Floats can be expressed in
scientific notation with e
between the significant figures and the powers of
ten. Python supports complex numbers. Purely imaginary decimal literals are
indicated with the suffix j
. Ordinarily, all numbers are expressed as
decimals, but one can use prefixes to denote hex (0x
), binary (0b
) or octal
(0o
) integers. Literal prefixes and suffixes cannot be mixed.
In [4]:
50 # 50 as a decimal integer
50.0 # 50 as a floating-point decimal
50. # 50 again as a floating-point decimal
5.0e1 # 50 as a floating decimal in scientific notation
50j # A purely imaginary 50
0x32 # 50 as a hexidecimal integer
0b110010 # 50 as a binary integer
0o62 # 50 as an octal integer
50
Math operations are intuitive. The basic math operators are:
Operator | Operation | Syntax | English Equivalent | Notes |
---|---|---|---|---|
+ |
Addition | a + b |
“a plus b” | Overloaded for built-in types |
- |
Subtraction | a - b |
“a minus b” | |
* |
Multiplication | a * b |
“a times b” | |
/ |
Division | a / b |
“a divided by b” | Floating-point True division: 3/5 == 0.6 |
// |
Floor Division | a // b |
“floor of (a divided by b)” | Floor division: 3/5 == 0; 5.0//2 == 2.0 |
% |
Modulus | a % b |
“a mod b” | |
** |
Exponentiation | a**b |
“a to the power b” | |
- |
Negation | -a |
“negative a” | Can be used to construct negative literals |
Math operators in Python work as they do in pen-and-paper math: they preserve
the most exact representation whenever possible. The can be checked using the
type
built-in function which returns the argument’s type. Again, the
possible types are integer, float, and complex.
In [5]:
type(5)
int
In [6]:
type(2)
int
In [7]:
type(5 + 2)
int
In [8]:
type(2.0)
float
In [9]:
type(2.0j)
complex
In [10]:
type(5 + 2.0)
float
In [11]:
type(5 + 2.0j)
complex
The order of operations follows the academic standard (PEMDAS: Parentheses, Exponents, Multiplication, Division, Addition, Subtraction):
In [12]:
5 * (2 + 3) - 3**2
16
Here, we meet parentheses again. Parentheses have a number of uses in Python. As earlier, they enclose function arguments. Here, they alter the order of operations. In general, they make logical groups of their contents. These groups always carry over new lines and are thus a way of splitting long lines:
In [13]:
(5 + 3
- 2)
6
In [14]:
type(5
+ 3 -2)
int
So far, we’ve seen the number literals, the math operators, and a few built-in functions. We have not yet seen any of the core keywords. Keywords are the heart of a programming language by which everything in the language can be written. Python has 33 core keywords.
Keywords | |
---|---|
Boolean Logic: | True , False , in , and , or , not |
Identities: | None , is |
Conditionals: | if , elif , else |
Flow Controls: | for , while , continue , pass , break |
Abstraction: | class , def , return , lambda , yield |
Namespaces: | import , from , as , with |
Excpetions: | try , except , raise , finally , assert |
Memory: | del , global , nonlocal |
Relax: you don’t need to memorize these now. We’ll learn them as we go. Less than half are used regularly and we’ll focus on only the most important ones.
Which are the important ones? Importance generally decreases from left to right and from top to bottom. Simple programs, for instance, rarely use the bottom two rows of commands. We’ll take time to discuss these keywords in detail later. I’m showing you the table now because there a few things we should learn from this list:
Having seen the keywords, we can take a look at assignment.
In programming, an object is a logical structure that encapsulates data and behaviors. Just like the word in English, an object is a ‘thing’ that can be created, manipulated, and destroyed. We can have many objects of the same type or class, but where each instance is unique.
Any object instance can be assigned to a variable name and in Python,
everything is an object. Assignment is governed principally by the binary =
operator.
The variable name given to an object must follow some rules:
print
).The variable name should follow certain conventions (see PEP8 for reasoning):
Names like “electron_mass” and “muon_energy” are better than “em” or “me”. No one wants to come back to code a year later and try to figure out what “em” and “me” mean in their code. Take time to write clear code now: your future self will thank you.
Assignment in Python can be thought of as giving an alias to an object
instance. You can give multiple aliases to the same object instance. To show
this, we can use the built-in function id()
which returns a unique number
associated with an object in memory (in fact, it’s derived from the object’s
memory address).
In [15]:
a_literal = 5 # Assign an instance of the literal '5' to the name 'a_literal'
print(a_literal, id(a_literal)) # print the value of 'a_literal' along with it's unique ID.
another_literal = a_literal # Assign the object assigned to 'a_literal' to 'another_literal'
print(another_literal, id(another_literal)) # Look at that: both 'a_literal' and 'another_literal' have the same ID.
(5, 21008696)
(5, 21008696)
As you can see, reassigning an object doesn’t create a new instance of the object. Instead, both variables point to the same object in memory. For those who are familiar with C and C++, assignment in Python is similar to references. Those of you who haven’t learned C or C++ yet will be well prepared having learned Python first.
The fact that assignment ‘aliases’ objects when possible can lead to a couple
serious ‘gotchas’ with so-called ‘mutable’ objects like lists
which we will
talk about later. Again, “Explicit is better than implicit”: if you want to
instantiate a new object on assignment, you must do so explicitly!
Finally, most objects upon which the math operators can act (like numbers and strings) support additional do-and-reassign operators. These can be used to compactify a statement. The set of all assignment operators is tabulated below.
Operator | Operation | Syntax | Description |
---|---|---|---|
= |
Assignment | a = b |
Assigns the name ‘a’ to the object ‘b’ |
+= |
Add and Reassign | a += b |
Equivilent to a = (a + b) |
-= |
Subtract and Reassign | a -= b |
Equivilent to a = (a - b) |
*= |
Multiply and Reassign | a *= b |
Equivilent to a = (a * b) |
/= |
Divide and Reassign | a /= b |
Equivilent to a = (a / b) |
%= |
Modulus and Reassign | a %= b |
Equivilent to a = (a % b) |
**= |
Exp and Reassign | a **= b |
Equivilent to a = (a**b) |
//= |
Floor Divide and Reassign | a //= b |
Equivilent to a = (a // b) |
Now that we know how to assign objects to variable names, we can talk about the core Python data structures. They are:
None
We’ve seen the numbers already. Let’s go through the rest in finer detail.
None
typeIn Python, None
is a special constant object that represents an explicit lack
of value. It is a null value. It has type NoneType
.
In [16]:
type(None)
NoneType
Functions with no explicit return value will return None
. Default arguments or
object attributes are often initialized as None
when not explicitly set. All
instances of None
are identically equal and are literally the same instance.
While None
is extremely useful, it can be somewhat abstract for beginners.
Don’t worry about it for now: future examples will make it clear how to make use
of None
. I only mention it now because it has certain implications for
Booleans.
Booleans are objects that have one of two possible values: true and false, 1 and
0, off and on, etc. The Python keywords include two boolean objects (True and
False) and a few boolean operators (in, and, or, not) that return either True
or False
.
Both True
and False
are objects of the type ‘bool’. Boolean types are
expected as arguments to some instructions like if
. Comparisons between
objects return boolean types.
In [17]:
type(True)
bool
However, other types can be interpereted as booleans in certain contexts. While
Python will cast a non-bool type into a bool on it’s own when necessary, we can
explicitly cast non-bool types into bools using the built-in bool
contructor.
In [18]:
bool(1), bool(0), bool(5), bool(-2), bool([]), bool([1, 2, 3]), bool(None)
(True, False, True, True, False, True, False)
So what’s happening here? bool(1)
and bool(0)
are understandably
interpereted as True
and False
respectively. But why is bool(5)
interperted as True? Because 5 is both a defined object and not explicitly
interpereted as False
. bool(-2)
is True for the same reason. All objects
that are defined and not strictly interpereted as False
are considered True
.
So what then is strictly considered False
? Objects numerically equivalent to
0, empty iterable sequences, and None
. This is a potential ‘gotcha’ when
checking if an object exists: you must make sure that it is not one of the
False
cases. In the above example, the objects []
and [1, 2, 3]
are
respectively empty and non-empty ‘iterables’ called ‘lists’. More on lists in a
moment.
The keyword not
negates a boolean.
In [19]:
not True, not False
(False, True)
The keyword in
tests for an object’s membership in an iterable sequence
(More on these in a moment). Below, we check to see if 1 is a member of the
‘tuple’ (1, 2, 3). It is, so True is returned.
In [20]:
1 in (1, 2, 3)
True
If we check to see if 5 is a member of the same sequence, Python tells us that is is not.
In [21]:
5 in (1, 2, 3)
False
The binary and
operator returns the logical conjunction of it’s operands. We
can have Python return the truth-table:
In [22]:
True and True, True and False, False and True, False and False
(True, False, False, False)
Likewise, the binary or
operator returns the logical disjunction of the
operands:
In [23]:
True or True, True or False, False or True, False or False
(True, True, True, False)
Strings are containers for text. The can be constructed in several ways:
str
.Why, you may ask, are there so many ways to write strings? Well, imagine if you want a quotation mark character in your string:
In [24]:
king_arthur = 'Arthur says "I am your king."'
print(king_arthur)
woman_responds = "Woman: 'Well I didn't vote for you.'"
print(woman_responds)
chock_full_of_quotes = '''"I've got to use a multiline string to handle all the quotes!"'''
print(chock_full_of_quotes)
string_from_a_number = str(2.54)
print(string_from_a_number)
Arthur says "I am your king."
Woman: 'Well I didn't vote for you.'
"I've got to use a multiline string to handle all the quotes!"
2.54
Notice how having several ways of denoting strings saves you from having to escape characters. Single line strings must open and close on a single line or be broken over lines using the ‘' character. Multi-line strings can span several lines. They will preserve carriage returns in the string.
In [25]:
long_string = 'The quick brown fox jumped \
over the lazy dog.'
print(long_string)
The quick brown fox jumped over the lazy dog.
In [26]:
the_squat_rack = '''allows for a free-weight workout using
a barbell without the movement restrictions imposed
by equipment such as the Smith machine.'''
print(the_squat_rack)
allows for a free-weight workout using
a barbell without the movement restrictions imposed
by equipment such as the Smith machine.
Strings are a type of iterable container and as such you can do some neat tricks with them.
In [27]:
("barbell" in the_squat_rack), ("curls" in the_squat_rack),
(True, False)
The in
keyword can act on strings. As iterables, strings have a length that
can be retrieved with the built-in function len
:
In [28]:
len(the_squat_rack)
130
The string the_squat_rack
is 130 characters long. We’ll learn more about
special iterable stuff when we discuss lists.
One important string operation is concatenation or joining two strings together. This can be done several ways. This first is just by adding them:
In [88]:
print(king_arthur + woman_responds)
Arthur says "I am your king."Woman: 'Well I didn't vote for you.'
Other ways include implicit joining, where if two string literals are one the same line, they are joined together:
In [87]:
'Some' "string is fishy here"
'Somestring is fishy here'
Strings in Python are objects (everything in Python is an object), and objects
can have associated functions attached to them called methods. In the case
of strings, these method functions simplify string-related operations. For those
who are new to object-oriented programming, we’ll explain methods in detail
later. For now, just think of methods as functions that are in some way bound to
an object. To see what methods and attributes an object has associated with it,
you can ‘introspect’ the object using the built-in dir
function.
In [29]:
dir(long_string)
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getnewargs__',
'__getslice__',
'__gt__',
'__hash__',
'__init__',
'__le__',
'__len__',
'__lt__',
'__mod__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rmod__',
'__rmul__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'_formatter_field_name_split',
'_formatter_parser',
'capitalize',
'center',
'count',
'decode',
'encode',
'endswith',
'expandtabs',
'find',
'format',
'index',
'isalnum',
'isalpha',
'isdigit',
'islower',
'isspace',
'istitle',
'isupper',
'join',
'ljust',
'lower',
'lstrip',
'partition',
'replace',
'rfind',
'rindex',
'rjust',
'rpartition',
'rsplit',
'rstrip',
'split',
'splitlines',
'startswith',
'strip',
'swapcase',
'title',
'translate',
'upper',
'zfill']
There are a lot of methods in a string object. To access an object’s method, you
write object_name.method_name
. The methods starting with double underscores
like __add__
are special. Methods named this way are ones that aren’t normally
called directly, even though you can. For example, __add__
is the method
called when you use the +
operator:
In [89]:
print(king_arthur.__add__(woman_responds)) # We don't normally call __add__ directly, since...
print(king_arthur + woman_responds) # ... __add__ is the internal name for the + operator.
Arthur says "I am your king."Woman: 'Well I didn't vote for you.'
Arthur says "I am your king."Woman: 'Well I didn't vote for you.'
We can find out more information about the string object’s methods using the
built-in help
function. Let’s look at the help for the __add__
, format
and
upper
methods:
In [92]:
help(long_string.format), help(long_string.upper)
Help on built-in function format:
format(...)
S.format(*args, **kwargs) -> string
Return a formatted version of S, using substitutions from args and kwargs.
The substitutions are identified by braces ('{' and '}').
Help on built-in function upper:
upper(...)
S.upper() -> string
Return a copy of the string S converted to uppercase.
(None, None)
Looking at the above output, we see that help
prints a string attached to the
method that explains what it does and how it does it. Notice also that the
return value of the help function is a an instance of None
meaning that all
the help function does is print the method’s documentation called a “docstring”.
All objects CAN have docstrings and it is good practice to include them in your
programming. They’re easy to make and greatly improve the readability of your
code. We’ll show how you can write them for your functions when we get there.
Notice that the docstrings show indicate that the method syntax included parentheses. What happens if we ommit the parentheses when trying to use a method?
In [31]:
long_string.upper
<function upper>
That didn’t do anthing because a method is a kind of function. As hinted at
earlier, to call a function requires parentheses around it’s arguments. If the
parentheses are omitted like above, Python thinks you want to manipulate the
method which is itself a type of object. Python tells us that the function upper
is, well, a function called upper. No surprises here. So to call the function,
we must wrap it’s arguments with parentheses. The docstring for string.upper
tells us the method takes no arguments, so to call it we write:
In [32]:
long_string.upper()
'THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG.'
So now you can find out what methods an object has and what they do with their
help docstrings. Some smart shells like IPython provide easy access to an
object’s methods and docstrings with tab completion. As an aside, format
is a
really useful string method, but it’s kind of advanced to use. We’ll come to it
again later.
Lists are among the most useful of Pythons built-in data structures. We’ve seen
some lists already. We can make a list by calling the list
constructor (a
built-in function) or by enclosing and ordered set of comma-separated objects in
square braces:
In [33]:
example_list = [] # An empty list
another_example = list() # Another empty list
constructed_list = list('Some string') # The list constructor takes an interable as it's argument.
some_numbers = list(range(10)) # A list of the numbers 0-9.
mixed_content_list = [1, 2.0, 'three'] # Notice that the contents needn't be the same type.
# We can even do wild things like
# a list inside a list inside a list inside a list!
listception = [1, 2, [3, 4, 5, [6, 7, 8, [9, 10]]]]
# Notice that the dimensions of the sublists needn't
# be the same! This is much like a tree graph.
Let’s see what we’ve done. Lists are iterables, so we can check a list’s length:
In [34]:
len(example_list)
len(another_example)
len(constructed_list)
len(mixed_content_list)
len(listception)
0
0
11
3
3
We can print the contents of a list:
In [35]:
print(some_numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
We can use iterable-aware keywords on a list:
In [36]:
2 in mixed_content_list
'two' in mixed_content_list
'three' in mixed_content_list
True
False
True
And we can access specific elements of a list. The following is true for all iterable objects (including strings). We do this by way of square braces containing the desired element’s index appended to an iterable object. In Python, iterable indexes start from 0!
In [37]:
# New school iterable awesomeness.
print(mixed_content_list[2])
three
This syntax is actually a means of using one those ‘hidden methods’ I told you
to ignore earlier. In this case, the []
syntax calls an object’s __getitem__
method, if it has one:
In [38]:
mixed_content_list.__getitem__(2)
'three'
We can get more information about __getitem__
using help:
In [39]:
help(mixed_content_list.__getitem__)
Help on built-in function __getitem__:
__getitem__(...)
x.__getitem__(y) <==> x[y]
The help docstring tells us that the two methods are equivalent. Another thing we can do with iterables is ‘slicing’. Slicing uses similar syntax to getitem, but a different argument: 2 indexes separated by ‘:’ in the special notation:
In [40]:
(some_numbers,
some_numbers[0:5], # Slice from index [0,5).
some_numbers[:5], # Another slice from index [0,5).
some_numbers[3:5], # A slice from index [3, 5).
some_numbers[3:10], # A slice from index [3, 10)
some_numbers[3:]) # A slice from index [3, infinity).
([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[3, 4],
[3, 4, 5, 6, 7, 8, 9],
[3, 4, 5, 6, 7, 8, 9])
Python can even interperet negative indexes as counting from the end of the list.
In [41]:
(some_numbers,
some_numbers[-2],
some_numbers[-3:],
some_numbers[:-3],
some_numbers[-5:-2],
some_numbers[3:-3])
([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
8,
[7, 8, 9],
[0, 1, 2, 3, 4, 5, 6],
[5, 6, 7],
[3, 4, 5, 6])
Be aware of this in case you use a malformed out-of-range index. Python lets you do a lot of things that would be considered “dangerous” in other languages (like C/C++). The philosophy behind this is summed up as:
We are all consenting adults here.
Basically, the language authors trust that you’ll be careful in exchange for having the freedom to do what you want.
Speaking of doing things that we want, We can insert, append, remove, pop, and sort lists (and most other iterables) using their methods:
In [42]:
method_examples = [] # Make an empty list.
print(method_examples)
method_examples.append('appended') # Append something to it.
print(method_examples)
method_examples.append(4) # Append something else.
print(method_examples)
method_examples.insert(1, 'insertion') # Insert something at index 1
print(method_examples)
popped_item = method_examples.pop() # Pop removes the last element from the list and returns it
print(popped_item)
print(method_examples)
method_examples.append(4) # Append some new stuff to the list.
method_examples.append(2) # Rinse and repeat.
print(method_examples)
method_examples.sort() # Sort in place.
print(method_examples)
# Remove doesn't take an index,
# instead it removes the first occurance of 4.
method_examples.remove(4)
print(method_examples)
[]
['appended']
['appended', 4]
['appended', 'insertion', 4]
4
['appended', 'insertion']
['appended', 'insertion', 4, 2]
[2, 4, 'appended', 'insertion']
[2, 'appended', 'insertion']
That sort is interesting. Python tries to be smart about the default case but,
when push comes to shove, you can override the sort method with your own
specific comparator. The messy list of operations above requires us to print the
list out after each operation because these methods all return None
and modify
the list ‘in-place’. This means that the list is ‘mutated’ without making a new
object. When an object supports this, it is called a ‘mutable object’ as opposed
to an ‘immutable object’ which cannot be changed without altering it’s location
in memory. lists
, dictionaries
, sets
and most objects are mutable;
numbers
, strings
, and tuples
are immutable. This has important
consequences for lists as we can see below:
In [43]:
first_list = [1, 2, 3, 4] # Let's make a new list. A new object is created.
second_list = first_list # Let's assign another name to that list.
second_list[2] = 1 # Let's mutate the second list by reassigning an item in it.
# What are the contents of 'first_list' and 'second_list' now?
print(first_list, second_list)
([1, 2, 1, 4], [1, 2, 1, 4])
Here we see that the assignment operation second_list = first_list
does not
copy the first list into a new object. Instead both names first_list
and
second_list
refer to the same object! Because lists are mutable, if you mutate
one, you’ll mutate the other because the two variables are just names for the
same object. If we wish to make a copy, we must do so explicitly:
In [44]:
first_list = [1, 2, 3, 4] # We create a new object.
second_list = list(first_list) # We use the list ctor to make a new list from the first one.
second_list[2] = 1 # Let's mutate the second list by changing an item.
# What are the contents of 'first_list' and 'second_list' now?
print(first_list, second_list)
([1, 2, 3, 4], [1, 2, 1, 4])
This is extremely important to be aware of, especially in large programs and especially in data analysis.
Tuples are another sort of iterable container. They are created by separating objects with commas:
In [45]:
my_tuple = 'a', 'tuple', 'is formed'
print(my_tuple)
('a', 'tuple', 'is formed')
You may have seen this done in previous examples to list multiple outputs on a
single line. Looking at the output, we see Python has put parentheses around the
output tuple. This is entirely superfluous: the parentheses only there to make
it clear where the tuple starts and stops. Notice then that the arguments to a
function are a tuple. The arguments to a list contructor like [1,2,3]
are a
tuple. There are other ways to construct a tuple, like using the tuple ctor,
which will make a tuple out of an iterable:
In [46]:
another_tuple = tuple(range(5))
print(another_tuple)
(0, 1, 2, 3, 4)
Our first example with tuples makes a single object (the tuple) out of three. We can go the other way making three objects from one using tuples:
In [47]:
one, two, three = my_tuple
print(one)
print(two)
print(three)
a
tuple
is formed
Any iterable can be cast over assignment to variables, as long as the number of variables created is equal to the length of the iterable. Let’s see it again:
In [48]:
one, two, three = 'wut'
print(one)
print(two)
print(three)
w
u
t
We’ll make use of this extensively when we get to loops. As with other iterables, we can access tuple elements by the get-item syntax:
In [49]:
my_tuple[2]
'is formed'
Tuples are immutable so unlike lists, we can’t change them once their made:
In [50]:
my_tuple[2] = 'is modified'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-50-fa5b9e48f84e> in <module>()
----> 1 my_tuple[2] = 'is modified'
TypeError: 'tuple' object does not support item assignment
Here we see the first example of a Python stack trace. When we issue a command
that gives rise to an error, the error is pointed out at runtime and a stack
trace is issued to help us debug the problem. Here, the error is a TypeError
.
The error occurs on line 1 at my_tuple[2] = 'is modified'
and the error tells
us that tuples don’t support item assignment. Fancy words for “tuples are
immutable types”. Strings are immutable iterables as well so while we can get a
string’s items, we can’t modify them:
In [51]:
print(long_string)
print(long_string[4])
long_string[4] = 'Q'
The quick brown fox jumped over the lazy dog.
q
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-51-d1bdc3471de6> in <module>()
1 print(long_string)
2 print(long_string[4])
----> 3 long_string[4] = 'Q'
TypeError: 'str' object does not support item assignment
Dictionaries are a type iterable container called a map. They use (key : value)
pairs to identify elements. They are unordered, which is to say they their
elements cannot be accessed by an index. Instead, elements are accessed by their
‘key’. To create a dictionary, we can use the dict
ctor or enclose a tuple of
(key : value) pairs in curly-braces. Keys are separated from their value by a
colon. Keys must be unique in the dictionary and must be immutable objects
(strings, number literals, tuples, etc.).
In [52]:
empty_dict = {}
what_colliders_collide = dict(lhcb='hadrons',
kekb='leptons',
tevatron=None)
mad_dict = {1:'first element',
'twos':2222,
'third key':[1, 2, 3],
(4, 4):'value mapped by a tuple'}
In [53]:
what_colliders_collide
{'kekb': 'leptons', 'lhcb': 'hadrons', 'tevatron': None}
In [54]:
mad_dict[1]
'first element'
In [55]:
mad_dict['twos']
2222
In [56]:
mad_dict['third key']
[1, 2, 3]
In [57]:
mad_dict[(4,4)]
'value mapped by a tuple'
To see list of keys in a dictionary, one can use the keys
method:
In [58]:
mad_dict.keys()
[1, (4, 4), 'twos', 'third key']
Notice that the keys are in a different order than how we constructed the dictionary. In a dictionary order does not matter. The usual sequence keywords apply to dictionary keys, but not values:
In [59]:
1 in mad_dict, (4, 4) in mad_dict, 2222 in mad_dict
(True, True, False)
Python sets
are the last principle data structure we’ll look at. Sets are
unordered lists of unique elements: Unlike other data structures we’ve seen,
elements cannot be repeated in a set. To ensure this, all the objects in a list
must be hashable. Sets lend themselves well to operations like unions,
intersections, differences, and symmetric difference. Sets are iterable, so are
subject to iterable-aware keywords like in
and len()
. Sets are created by
enclosing comma-separated objects in curly braces or via the set
ctor, which
like other data structure ctors we’ve seen takes an iterable as it’s argument.
Note that the difference between set construction and dictionary construction is
the lack of the colon denoted (key : value) pairs used in dictionaries.
In [60]:
first_set = {1, 2, 3}
second_set = set((-1, 0, 1, 2))
third_set = {1, 2}
Set methods overload many of the math operators to do what you would expect in a
math class. For instance, set intersection can be mediated by the &
operator:
In [61]:
first_set & second_set, first_set.intersection(second_set)
({1, 2}, {1, 2})
Set union can be mediated by the |
operator.
In [62]:
first_set | second_set, first_set.union(second_set)
({-1, 0, 1, 2, 3}, {-1, 0, 1, 2, 3})
Set difference is mediated by the -
operator.
In [63]:
first_set - second_set, first_set.difference(second_set)
({3}, {3})
Symmetric difference is mediated by the ^
operator.
In [64]:
first_set ^ second_set, first_set.symmetric_difference(second_set)
({-1, 0, 3}, {-1, 0, 3})
And testing whether one set is a subset or superset of another can be done with the comparison operators, which will talk about next.
In [65]:
first_set.issubset(third_set), first_set.issuperset(third_set)
(False, True)
In [66]:
first_set <= third_set, first_set >= third_set
(False, True)
So far we have discussed the basic syntax for writing instructions in Python.
We have learned about basic numbers and math operations in Python. We have seen
how to assign variables and saw some of the caveats to assignment concerning
mutable objects. We are aware of the None
object and have seen it appear as
output. We have discussed the most salient features of the core data structures:
Booleans, Strings, Lists, Tuples, Dictionaries, and Sets. And we’ve discussed
some of the core philosophies in Python such as “Explicit is better than
implicit” and “We’re all consenting adults.”
Next we will learn about the comparison operators, conditionals/branching, and loops.