Objects are pieces of code that are designed to be interchangeable and reused.
Data types are dynamic based on the variable stored. To check type, use:
# python2
x = 10
type(x)
# output: <type 'int'>
# python3
# output: <class 'int'>
Notice the output is <class 'int'> or <type 'int'>.... Type and class are interchangeable for our purposes. There was a time when they wern't. Once again, everything in Python is an object! We will go over type() more in meta-classes.
Multiple assignment is allowed and types can be the same or different:
# All == 100
a = b = c = 100
# Will only reassign variable c
c = 200
print ('a = {} b = {} c = {}'.format(a, b, c))
# output: a = 100 b = 100 c = 200
# will change all three variables
a,b,c = 100, 'hello', {}
print ('a = {} b = {} c = {}'.format(a, b, c))
#output: a = 100 b = hello c = {}
| Class | Description | Immutable? |
|---|---|---|
| bool | Boolean Value | ✓ |
| int | integer(arbitrary magnitude) | ✓ |
| float | floating-point number | ✓ |
| list | mutable sequence of objects | |
| tuple | immutable sequence of objects | ✓ |
| str | character string | ✓ |
| set | unordered set of distinct objects | |
| frozenset | immutable form of set class | ✓ |
| dict | associative mapping(dictionary) |
Prefixes convert types like binary, hex and octal into int.
$ py -3
>>> 0b10
2
>>> 0xF
15
>>> 0o100
64
>>> x = 0xF
>>> type(x)
<class 'int'>
>>> x
15
x = 1.5 + 5j
y = 2
z = x + y
print(z)
#Output: (3.5+5j)
z = z + 3j
print(z)
#Output: (3.5+8j)
Numbers cannot be modified in place. Be sure to either reassign your current variable or assign the number to a new variable.
$ py -3
>>> x = 10
>>> print(x)
10
>>> x = 15
>>> print(x)
15
>>> x = x + 5
>>> print(x)
20
>>> x + 5
25
>>> print(x)
20
Bools are a subclass of int. This was done around Python 2.2 to allow previous implementations of bools (0 and 1) to continue working… especially so with C code that utilizes Pylint_Check()
| Operator | Description | Example | Result |
|---|---|---|---|
| + | Addition | 4 + 5 | 9 |
| - | Subtraction | 10 - 5 | 5 |
| * | Multiplication | 4 * 2 | 8 |
| / | Division | 3/2 | Py2(1) Py3(1.5) |
| // | Floor Division | 5.0 // 2.0 | 2.0 |
| % | Modulus(remainder) | 4 % 2 | 0 |
| ** | Exponentiation | 4 ** 2 | 16 |
Bitwise Operators directly manipulate bits.
| Operator | Description | Example | Result |
|---|---|---|---|
| & | Binary AND | 1 & 3 | 0001 & 0011 == 0001 (1) |
| | | Binary OR | 1 | 3 | 0001 | 0011 == 0011 (3) |
| ^ | Binary XOR | 1 ^ 3 | 0001 ^ 0011 == 0010 (2) |
| ~ | Binary Ones Complement | ~3 | ~00000011 == 11111100 |
| << | Binary Left Shift | 1 << 3 | 00000001 << 3 == 00001000 |
| >> | Binary Right Shift | 1 >> 3 | 00000001 >> 3 == 00000000 |
| Operation | Precedence | Extra |
|---|---|---|
| 0 | 1 | Anything in brackets is first |
| ** | 2 | Exponentiation |
| -x, +x | 3 | N/A |
| *, /, %, // | 4 | Will evaluate left to right |
| +, - | 5 | Will Evaluate left to right |
| Operation | Precedence | Extra |
|---|---|---|
| <, >, <=, >=, !=, == | 6 | Relational Operators |
| Logical Not | 7 | N/A |
| Logical And | 8 | N/A |
| Logical Or | 9 | N/A |
# int(x, base)
## Returns x as 'base' integer.
##'base' specifies base if x is string, otherwise optional
### EXAMPLE 1
x = 10.5
int(x)
# output: 10
### EXAMPLE 2
y = "101101"
int(y, 2) # specifiy y as a base 2
# output: 45
# float(x)
## Returns x as a float
x = 30
float(x)
# output: 30.0
#########################
# complex(real, imag)
## Returns complex number, defaults for real/imag is 0
complex(2, -3)
# output: (2-3j)
complex(2, 3)
# output: (2+3j)
# chr(x)
## Returns string of one character for x as ASCII
x = 10
chr(x)
# output: '\n'
x = 10.5
chr(int(x)) # notice what we did here?
# output: '\n'
#########################
# ord(x)
## Returns ASCII value for x as string of one char
ord('\n') # notice what we did here?
# output: 10
# hex(x)
## Returns x as hex value
hex(10)
# output: '0xa'
## Notice how the output is a string, there is no hex type
#########################
# oct(x)
## Returns x as octal
oct(10)
# output: '012'
## Same thing here
#########################
# bin(x)
## Returns x as binary
bin(10)
# output: '0b1010'
## Same thing here
A sequence object is a container of items accessed via index. Text strings are technically sequence objects.
Each of the above support built in functions and slicing.
Strings are immutable! They need to be reassigned. There are two independent types of strings:
To declare a string, use one of the following. There is no Pythonic way aside from keeping it as simple and clean as possible.
Yes, this stuff is old school and not very Pythonic... but it can't be helped.
>>> x = "I am a string"
>>> type(x)
<type 'str'>>
>>> x
'I am a string'
>>> hex(ord(x[0]))
0x49
>>> x = 'I am a string'.encode('ASCII')
>>> type(x)
<class 'bytes'>
>>> print(hex(x[0]))
0x49 # ASCII code for capital I
>>> x = b'I am a string' # Doing this in Python 2 makes no difference
>>> type(x)
<class 'bytes'>
>>> x = u"I am a unicode string"
>>> type(x)
<type 'unicode'>>
>>> y = unicode("Look at me! I’m a unicode string.")
>>> type(y)
<type 'unicode'>
# You can use ‘…’ quotes “…” quotes or “””…””” quotes
>>> x = 'I am a unicode string'
>>> type(x)
<class 'str'>
# class str is unicode in Python 3 natively
# You can use ‘…’ quotes “…” quotes or “””…””” quotes
>>> x = "hello world"
>>> print(x[4]) # grabs 4th index
o
>>> x = "hello world"
# grabs 4th index up until, but not including the 9th index.
>>> print(x[4:9])
o wor
>>> x = "hello world"
>>> print(x[-3]) # grabs the 3rd to the last ELEMENT
r
>>> my_string = "Hello World!"
>>> print(my_string[0])
H
>>> print(my_string[0:5])
Hello
>>> print(my_string[6:])
World!
>>> print(my_string[-6:]) # ???
>>> print(my_string[::2]) # ???
>>> print(my_string * 2) # ???
>>> print(my_string + my_string) # ???
What did you get? Try different operations to manipulate strings!
| Operator | Description |
|---|---|
| + | Concatenation |
| * | Repetition |
| [] | Slice |
| [:] | Range Slice |
| in | Returns true if a character exists |
| not in | Returns true if a character does not exist |
| % | Format |
There are a few ways to capture user input in Python.
It is important that you use raw_input() in Python 2
>>> name = raw_input("What is your name? ")
What is your name? <user input>
>>> name
<user input>
>>> name = input("What is your name? ")
What is your name? <user input>
>>> name
<user input>
As you more than likely guessed, the user input is assigned to the variable name and stored as a string. From here, you can treat var name as any other regular string.
>>> my_string = "Hello World!”"
>>> a = my_string.upper()
>>> print(a)
'HELLO WORLD!'
>>> a = my_string.lower()
>>> print(a)
'hello world!'
>>> a = len(my_string)
>>> print(a)
12
>>> a = my_string.split(" ")
['Hello', 'World!']
>>> print(my_string)
'Hello World!'
>>> my_string = "+".join(a)
>>> print(my_string)
'Hello+World!'
>>> my_string = "Hello World!"
>>> my_string[1]
'e'
>>> my_string[1] = 'E'
join() combines sequential string elements by a specified _str _separator. If no separator is specified join will insert white space by default.
s = "-";
seq = ("a", "b", "c"); # This is sequence of strings.
print(s.join( seq ))
#Output: a-b-c
split() will breakup a string and add the data to a string array using a defined separator.
x = 'blue,red,green'
x.split(",")
['blue', 'red', 'green']
>>>
>>> a,b,c = x.split(",")
>>> a
'blue'
>>> b
'red'
>>> c
'green'
>>> my_list = ['Hello World', 15, True, 'w']
>>> nested_list = [['such', 'wow'], 5, [False, '15']]
Much like strings, you can slice lists. There are some differences though. Slicing only one element will return a substring. Whereas slicing a range of elements will return another list.
>>> my_list = ['apple', 'orange', 'cherry', 'strawberry']
>>> my_list[3]
'strawberry'
>>> type(my_list[3])
<class 'str'>
>>> nested_list = [['apple', 'orange'], ['onion', 'pepper']]
>>> nested_list[3] # ???
>>> my_list[0:2]
['apple', 'orange', 'cherry'] # outputs list
>>> type(my_list[0:2])
<class 'list'>
>>> nested_list[0][1]
'orange'
>>> nested_list[0][0:2]
['apple', 'orange']
>>> nested_list[0:2][1] # ???
index() will output the index (starting at 0) of an element that matches the index() argument. index() looks for strict matches. Overall, this is useful for finding the index of a specific item. Example below.
>>> my_list = ['Hello World', 15, True, 'w']
>>> my_list.index(15)
1
>>> my_list.index('Hello')
Traceback (most recent call last):………
# index method looks for strict matches, ValueError: 'Hello' is not in list
# useful for finding index of a specific item
Works just like index()
>>> True in my_list
True
>>> 'Hello' in my_list
False
>>> 20 not in my_list
True
Rememeber, lists are MUTABLE! This simply means we can modify it in place via appending, removing, combining, etc.
>>> my_list = [1,2,3,4,5]
>>> my_list.append(6)
[1, 2, 3, 4, 5, 6]
>>> my_list[0] = 99
>>> my_list
[99, 2, 3, 4, 5, 6]
>>> my_list.insert(0, 1)
>>> my_list
[1, 99, 2, 3, 4, 5, 6]
>>> messy_list = [2,1,4,5,3,5,100,222,44]
>>> sorted(messy_list)
# ??????
>>> print(messy_list)
>>> messy_list.sort()
# ??????
>>> print(messy_list)
>>> messy_list.sort(reverse=True)
>>> messy_list
# ??????
>>> print(messy_list)
>>> rally_cars = ['Subaru', 'Ford’]
>>> rally_cars.extend(['Mini', 'Audi'])
>>> rally_cars
['Subaru', 'Ford', 'Mini', 'Audi']
>>> rally_cars += ['Peugeot', 'MG Metro']
>>> rally_cars
['Subaru', 'Ford', 'Mini', 'Audi', 'Peugeot', 'MG Metro']
# remove()
>>> my_list = [1,2,3,3,4,5]
>>> my_list.remove(3)
>>> my_list
[1, 2, 3, 4, 5]
# del
>>> del my_list[2]
>>> my_list
[1, 2, 4, 5]
# pop()
>>> my_list.pop(1)
2 # value popped
>>> my_list
[1, 4, 5]
Map() applies a function to all the items in an input_list.
map(function_to_apply, list_of_inputs)
items = [1, 2, 3, 4, 5]
squared = []
for i in items:
squared.append(i**2)
map() allows us to implement this in a much simpler way
items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))
Don't fret over what lambda's are just yet. Just know they are condensed functions for now
def multiply(x):
return (x*x)
def add(x):
return (x+x)
funcs = [multiply, add]
for i in range(5):
value = list(map(lambda x: x(i), funcs))
print(value)
# Output:
# [0, 0]
# [1, 2]
# [4, 4]
# [9, 6]
# [16, 8]
filter() - creates a list of elements for which a function returns true.
number_list = range(-5, 5)
less_than_zero = list(filter(lambda x: x < 0, number_list))
print(less_than_zero)
# Output: [-5, -4, -3, -2, -1]
The filter resembles a for loop but it is a builtin function and faster.
reduce() - performs some computation on a list and returns the result. It applies a rolling computation to sequential pairs of values in a list.
number_list = range(-5, 5)
less_than_zero = list(filter(lambda x: x < 0, number_list))
print(less_than_zero)
# Output: [-5, -4, -3, -2, -1]
The filter resembles a for loop but it is a builtin function and faster.
Computing the product of a list of integers
product = 1
list = [1, 2, 3, 4]
for num in list:
product = product * num
# product = 24
Using reduce:
from functools import reduce # we will cover this later
product = reduce((lambda x, y: x * y), [1, 2, 3, 4])
# Output: 24
# Method 1 (shortest)
>>> x = b'hello world'
>>> print(x[1])
101 # ASCII dec value
# Method 2 (clean)
>>> x = 'hello world'
>>> y = bytes(x, 'ascii')
>>> print(y)
b'hello world'
>>> print(y[1])
101 # ASCII dec value
# python 2
>>> x = 'hello world'
>>> print ord(x[1])
# ord is the inverse of chr()... returns int representing the unicode code point of the argument
101 # Unicode dec value
This can go both ways. We can convert these integer representations back into Unicode or ASCII strings.
# Python 2 back to ascii string
>>> x = ord('e')
>>> x
101
>>> y = chr(x)
>>> y
'e'
# convert y into a unicode string (this only works in Python 2 because unicode is default in Python 3)
>>> y = unichr(x)
>>> y
u'e'
# Python 3 back to native unicode string
>>> x = ord('e')
>>> x
101
>>> y = chr(x)
>>> y
'e'
# y is now a unicode string... but how do we turn it into a byte/ascii string?
>>> y = bytes(y, 'ascii')
>>> y
b'e'
# Now y is a python 2 str type... bytes/ascii
>>> x = "I am a string"
>>> b = bytearray()
>>> b.extend(x)
# This was done on purpose to show it is mutable
# You can pass the str directly into the bytearray() function to cut 2 lines
>>> b
bytearray(b'I am a string')
>>> b[2]
97 # decimal for 'a' char
>>> b[2] = 85 # Modifying a byte
>>> b
bytearray(b'I Um a string') # notice b did change without reassignment
>>> b = bytearray(b"I am a string")
>>> b
bytearray(b'I am a string')
>>> b[2]
97 # decimal for 'a' char
>>> b[2] = 85
b
bytearray(b'I Um a string')
# common method to declare tuples
>>> someTuple = (1,2,3,4,5)
# alternative method
>>> neatTuple = 1,2,3,4,5
>>> print neatTuple
(1, 2, 3, 4, 5)
# What does the below do?
>>> del someTuple[2]
Tuples are still sequence objects. You can still:
Useful for:
range() represents an immutable iterable object that always takes the small and same amount of memory irrespective of the size of range because it only stores start, stop, and step values and calculates values as needed.
range(stop) range(start, stop,[ step])
start: Required when full syntax is used. An integer specifying start value for the range.
stop: Required. The boundary value for the range.
step: Optional. Step value.
>>> range(4)
range(0,4)
# if we want to see what is contained within our range
>>> list(range(4))
[0, 1, 2, 3]
>>> list(range(2,6))
[2, 3, 4, 5]
>>> list(range(0,50,5))
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45]
>>> list(range(4,12,3))
# ???
>>> list(range(0,-10,-2))
# ???
We will cover range() more in depth and use it a lot more when we get to loops.
xrange() is from Python 2 and is similar to range(), returns xrange object (sequence object) instead of list.
>>> for i in xrange(10):
>>> print i
0
1
2
3
4
5
6
7
8
9
# Only even numbers
>>> for i in xrange(2, 10, 2):
>>> print i
2
4
6
8
# Negative numbers
>>> for i in xrange(-1, -10, -1):
>>> print i
-1
-2
-3
-4
-5
-6
-7
-8
-9
Buffer (memoryview) is useful if you don’t want to or can’t hold multiple copies of data in memory. It can also be lightning fast since it's not copying the data. Buffer (or memoryview) essentially expose (by reference) raw byte arrays to other Python objects. That means the argument passed must be in bytes (ints representing bytes).
# Python 2
>>> x = b'100'
>>> buffer(x)
<read-only buffer for 0x10b1acc60, size -1, offset 0 at 0x10b1a9930>
# Python 3
>>> x = b'100'
>>> memoryview(x)
<memory at 0x1040b1948>
import time
for n in (100000, 200000, 300000, 400000):
data = 'x'*n # set data = 'x'*n (if n were 5, xxxxxx)
start = time.time() # start a timer
b = data # set b = data
while b: # remove one x and reasign to b, continue until 0
b = b[1:]
print 'bytes', n, time.time()-start # stop time, print out time it took to do operation
# Same thing here, except we use memoryview instead
for n in (100000, 200000, 300000, 400000):
data = 'x'*n
start = time.time()
b = memoryview(data)
while b:
b = b[1:]
print 'memoryview', n, time.time()-start
>>> my_dict = {} # create empty dictionary
>>> my_dict['one'] = 1 # add item to the dictionary
>>> print my_dict
{'one': 1}
# OR
>>> my_dict = {'one' : 1} #create a dictionary with an item
>>> print my_dict
{'one': 1}
>>> my_dict['two'] = 2 # add item to the dictionary
>>> print my_dict
{'one': 1, 'two': 2}
# Grabbing by key
>>> new_dict = {'key1':'value1','key2':'value2','key3':'value3'}
>>> print new_dict['key2']
'value2'
Just like lists... Dictionaries can be nested as well to create a multi-dimensional dictionary.
# Dict -> Dict
>>> my_dict = {'key1':{'nestedkey1':{'subnestedkey1':'subnestedValue'}}}
>>> print my_dict
{'key1':{'nestedkey1':{'subnestedkey1':'subnestedValue'}}}
# Grab key 1's value
>>> print my_dict['key1']
{'nestedkey1': {'subnestedkey1': 'subnestedValue'}}
# Grab nested key 1's value
>>> print my_dict['key1']['nestedkey1']
{'subnestedkey1': 'subnestedValue'}
# Grab subnested key 1's value
>>> print my_dict['key1']['nestedkey1']['subnestedkey1']
subnestedValue
>>> d[i] = y # value of I is replaced by y
>>> d.keys() # grabs all keys
>>> d.values() # grabs all values
>>> d.clear() # removes all items
>>> d.copy() # creates a shallow copy of dict_x
>>> d.fromkeys(S[,v]) # new dict from key, values
>>> d.get(k[,v]) # returns dict_x[k] or v
>>> d.items() # list of tuples of (key,value) pairs
>>> d.iteritems() # iterator over (key,value) items
>>> d.iterkeys() # iterator over keys of d
>>> d.itervalues() # iterator over values of d
>>> d.pop(k[,v]) # remove/return specified (key,value)
>>> d.popitem() # remove/return arbitrary (key,value)
>>> d.update(E, **F) # update d with (key,values) from E
A set is an unordered collection of unique elements. Sets are mutable but contain no hash value-- so they can't be used as dict keys or as an element of another set.
>>> new_set = set() # create an empty set
>>> new_set = {0, 1, 1, 1, 2, 3, 4}
>>> new_set
{0, 1, 2, 3, 4}
>>> new_set.add(5) # Add new key to set
>>> new_set
{0, 1, 2, 3, 4, 5}
>>> x_set = set("This is a set")
>>> x_set
{'s', 't', 'e', 'h', 'I', ' ', 'T', 'a'}
>>> another_set = set(['Ford', 'Chevy', 'Dodge', 105, 555])
{'Chevy', 105, 155, 'Ford', 'Dodge'} # many ways to create set
Frozensets are identical to sets aside from the fact that they are immutable. Since frozensets are immutable, they are hashable as well. So they can be used as a dict key or element of another set.
>>> new_set = frozenset([1,2,2,2,3,4])# create an frozenset
>>> new_set
frozenset({1, 2, 3, 4})
>>> new_set.add(5)
.......AttributeError: ‘frozenset’ object has no attribute ‘add’
# Doesn't work since Frozenset is immutable
# Many… many ways to create frozenset as well.
Some do not apply to Frozenset
>>> s.issubset(t) # test if elements in s are in t
>>> s.issuperset(t) # test if elements in t are in s
>>> s.union(t) # new set with elements of t and s
>>> s.intersection(t) # new set with common elements
>>> s.difference(t) # new set with elements in s but not t
>>> s.symmetric_difference # xor
>>> s.copy() # new shallow copy of s
>>> s.update(t) # return set s with elements from t
>>> s.intersection_update(t)
>>> s.difference_update(t)
>>> s.symmetric_difference_update(t)
>>> s.add(x) # add x to set s
>>> s.remove(x) # remove x from set s
>>> s.discard(x) # remove x from set s if present
>>> s.pop() # remove arbitrary item
>>> s.clear() #remove all elements from set a
Below are some functions to convert a variable to another type.
>>> int()
>>> long()
>>> float()
>>> complex()
>>> str()
>>> repr()
>>> eval()
>>> tuple()
>>> list()
>>> set()
>>> dict()
>>> frozenset()
>>> chr()
>>> unichr()
>>> ord()
>>> hex()
>>> oct()
>>> bin()
Sys is a large module from the Standard Library that contains very useful code and functionality. We will get into what modules are and how to import and such later in the course. For now, we are going to focus on one function from the module.
sys.getsizeof(object) --gets the size of the object passed in bytes. As you can imagine, this is going to be very helpful in streamlining your code.
>>> import sys
>>> x = 40
>>> sys.getsizeof(x)
12