- Create file with simply:
print("Hello World!")-
Note simplicity, how that's handy for starting out with programming
- Only introduce concepts as needed
-
print()is a chunk of code written by someone else- Something you're going to be doing alot
- Figure out the solution and reuse it
-
Note you can print out multiple comma-separated items
-
"Hello World!"is a string- Can also be
'single quoted' - Or
"""TRIPLE QUOTED"""
- Can also be
-
Example of Python scripts and interpreter to highlight use of
"""'s\nand\tescape sequences- Escaping quotes within strings
\\to print a literal backslash
-
Name on LHS, value on RHS
=is used, but not setting both sides to be equal- Assignment operator, not equality operator
- Putting a constant on LHS will raise error
-
Naming rules:
- Can't use a keyword
- No spaces
- Must start will
a-zA-Z_ - After that, can contain
a-zA-Z0-9_ - Case sensitive
input()examples
- Indexing individual characters
- Substring
- From some point to end
- From beginning to some point
- From beginning to end
- Negative indices
- Can also be used as range bounds
+concat*repeatlen()will tell you how many characters are in the string
int- Number with no fractional component
float- Number with fractional component
- Note that variables can be reassigned new values
- Type is bound to the value, not to the variable
- VERY different from many other languages (e.g., C, Java)
- Will be odd for programmers in other languages
- Will be intuitive for new programmers
- This is dynamic typing
- Still strongly typed
- Can't concat str and int
str()can be used to convert int/float to str- Or any other object...
- More on that later
- Or any other object...
int()to parse an integer from a stringfloat()to parse a float from a string
s = input("Enter a string to repeat: ")
t = input("How many times should it be repeated? ")
ti = int(t)
r = s * ti
print(r)- First, remove
tiand just reassign the value ofint(t)tot - Then, turn the
input()andint()calls into a one-liner - Then, turn the
print()and repeat ops into a one-liner - Highlight the need to maintain readability
- Used to represent the absense of a value
- If you want to define a variable, but don't have a value for it yet, use
None
+add-subtract*multiply/divide- Demonstrate int/int yeilds a float
//floor division (aka integer division)
%modulo (aka mod, aka remainder)**exponentiation- Order of operations:
()***,/,//,%+,-- Tied operators eval'd left to right
- Example:
16 - 2 * 6 // 3 + 1- == 13
- Tie this back to computational thinking!
- Break the complicated overall problem into subproblems
- Any parens? Nope!
- Any exp? Nope!
- Any mul/div? Yes! Eval L -> R
- Finally, eval add/sub L -> R
- Break the complicated overall problem into subproblems
- Note that if
//is changed to/, the result becomes float- When one operand if float, result is float
- Incrementing a variable:
x = x + 1- A bit too verbose, so we have:
+=-=*=/=%=
- Any C/Java programmers?
- Note no
++--
- Note no
- Returns a string
format(a_float, [total][,][.after_dec]f)format(an_int, [total][,]d)- If length of string is less than total, right aligned and padded with spaces
sep=end=
- Say you're writing a tax calculator for a 7% sales tax, why would this be a bad idea:
total = subtotal * .07- If the tax rate changes, you need to update the
.07wherever its used! - Do this instead:
TAX_RATE = .07
total = subtotal * TAX_RATE- Now, you'd just have to update the one instance of
TAX_RATE - In general, it is good practice to avoid unexplained literals
- THE ALL CAPS IS CONVENTION TO INDICATE A CONSTANT VALUE
- Not actually guaranteed constant, just a variable
- Python doesn't support formal constant values
- Many other languages will for exactly this purpose
- Only two possible values:
TrueFalse
- Yes, caps are necessary
- Take in values, return a boolean
==!=<><=>=
- Simple for int/float, what about strings?
- lexicographical order used for inequality ops
- Basic
if- If boolean expression between
ifand:evals to true, execute body - Note that the "body" is a code block
- Defined by indentation
- Tabs vs spaces
- If boolean expression between
if num == 1:
print("one!")if...else
if num == 1:
print("one!")
else:
print("not one...")- Nested
if's- What happens if there is an
ifstatement in the body of anotherifstatement?- No problem!
- What happens if there is an
color = "blue"
if color[0] == "b":
if color == "blue":
print("blue!")
else:
print("something else with a \"b\"")
else:
print("not blue!")if...elif...else
if num == 1:
print("one!")
elif num == 2:
print("two!")
elif num == 3:
print("three!")
else:
print("not one or two or three...")- Boolean operators
andornot- Precedence:
notfirstandnextorlast- All lower priority than comparison operators
- All lower priority than math operators
- Truth tables
- Complex logic expression
- Another example of computational thinking
(True and False or False or not False) and not (False or False)- Enter only if condition is
True - After completing loop body, recheck condition and, if still
True, execute body again - EX:
count = 0
k = "y"
while k == "y":
count += 1
print("I've looped", count, "times!")
k = input("Keep going? ")- EX:
i = 0
while i < 10:
print(i)
i += 1- Beware!
count = 0
k = "y"
while k == "y":
count += 1
print("I've looped", count, "times!")- EX:
num = int(input("Enter a number to square (-1 to stop): "))
while num != -1:
print(num, "squared is:", num ** 2)
num = int(input("Enter a number to square (-1 to stop): "))-1is our sentinel- Note this is different from the 10 in the previous example as we don't know how many iterations this loop will go through
- EX:
num = int(input("Enter a number between 1 and 10: "))
while num < 1 or num > 10:
print("!! ERROR:", num, "is not between 1 and 10 !!")
num = int(input("Try again: "))
print(num, "is between 1 and 10!")- Sentinel + input validation
num = int(input("Enter a number between 1 and 10 to square (-1 to stop): "))
while num == 0 or num < -1 or num > 10:
print("!! ERROR:", num, "is not between 1 and 10 !!")
num = int(input("Try again: "))
while num != -1:
print(num, "squared is:", num ** 2)
num = int(input("Enter a number between 1 and 10 to square (-1 to stop): "))
while num == 0 or num < -1 or num > 10:
print("!! ERROR:", num, "is not between 1 and 10 !!")
num = int(input("Try again: "))- Immediately exit the current loop
while True:
num = int(input("Enter a number between 1 and 10 to square (-1 to stop): "))
while num == 0 or num < -1 or num > 10:
print("!! ERROR:", num, "is not between 1 and 10 !!")
num = int(input("Try again: "))
if num == -1:
break
print(num, "squared is:", num ** 2)-
Only going in to example with
range()for now- Because Gaddis wants to cover functions before collections...
- Discuss your thoughts on this
- Because Gaddis wants to cover functions before collections...
-
# NEVER FORGET- Easy to write malformed basic iteration, count bound
whileloops forsolves this problem
- Easy to write malformed basic iteration, count bound
for i in range(10):
print(i)for i in range(2, 8):
print(i)for i in range(1, 10, 2):
print(i)for i in range(10, 2, -3):
print(i)- Note that while this is a common use of
for, it is MUCH MORE POWERFUL than this
- Executed when you reach the end of what you're interating over
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, "equals", x, "*", n // x)
break
else:
print(n, "is a prime number")- Can also be applied to
while- Executed whenever condition is
False
- Executed whenever condition is
- Skip to the next iteration of the current loop
for num in range(2, 10):
if num % 2 == 0:
print("Found an even number", num)
continue
print("Found a number", num)- Draw a left-tipped triangle
for i in range(1, 6):
for j in range(i):
print("*", end="")
print()- Draw a right-tipped triangle
LIMIT = 6
for i in range(1, LIMIT):
for j in range(LIMIT - i - 1):
print(" ", end="")
for k in range(i):
print("*", end="")
print()- Draw a middle-tipped triangle
LIMIT = 10
for i in range(1, LIMIT, 2):
sp = (LIMIT // 2) - ((i // 2) + (i % 2))
for j in range(sp):
print(" ", end="")
for k in range(i):
print("*", end="")
print()- Draw a diamond
TBA- Deduplication of code
- Ease of reuse
- Helps in breaking down complex problems
- Others... (that aren't of much use in an intro course...)
- Testing
- Faster dev
- Ease teamwork
def message():
print("Hello there!")
print("How are you?")- Using name defined with
defimmediately followed by parenthesis
message()def main():
print("===START MESSAGE===")
message()
print("===END MESSAGE===")
def message():
print("Hello there!")
print("How are you?")
main()- Trace through this example
- NEED ALL KINDS OF EXAMPLES HERE
- CAN WING IT
- Can define variables within a function, just like anywhere else
- Can't access variables defined in one function within another
- Different scope
- Can even have two different functions with variables with the same name
- Can't access variables defined in a function within the calling context
- Or vice versa, with the exception of globals (no spoilers!)
- Defined outside of any function
- Available in all scopes in current file (or interpreter session)
- Bad practice to make use of global variables
my_var = None
def record_score():
my_var = int(input("Enter a score: "))
def repeat_score():
print("Current score is:", my_var)
def print_message():
my_var = "Hello" + " " + "There" + "!"
print(my_var)- Global scope variables used as constants are fine
- E.g.,
TAX_RATEfrom awhile ago
- E.g.,
- Function arguments
- Defined in the
()of thedefline - "Passed in" to the function when included in the
()of the function call
- Defined in the
def say_hello(name):
print("Hello,", name, "!")-
Local in scope to the function that are tied to
-
If their value is changed in the body of the function, acts just like a local variable
-
Keyword arguments
- Specify a default value for the parameter
- Can be omitted when calling the function
- Can be mixed with positional parameters
- Can have a value set during calling by either:
- Placing a value at the appropriate position in actual parameter list
- Using
keyword=valuein actual parameter list
- Note formal vs actual parameter semantics
- NEED TO WING IT WITH MORE EXAMPLES HERE
- If we want to encapsulate some computation within a function, we need a way to return the results
- Just like
input()! - Aptly, we can use
return
- Just like
def repeat(name, times):
return name * times
result = repeat("nick", 5)def get_num():
x = int(input("Enter a number between 1 and 10 to square (-1 to stop): "))
while x == 0 or x < -1 or x > 10:
print("!! ERROR:", x, "is not between 1 and 10 !!")
x = int(input("Try again: "))
return x
num = get_num()
while num != -1:
print(num, "squared is:", num ** 2)
num = get_num()-
Note how we can change x to num in that example, it doesn't matter because of different scopes
-
Write a
check_num()function that returns a boolean -
Empty return vs no return
- All None:
def no_ret():
print("No return")
def empty_ret():
print("Empty return")
return
def none_ret():
print("None return")
return None
print("None is")
print(type(None))
nr = no_ret()
print(type(nr))
er = empty_ret()
print(type(er))
noner = none_ret()
print(type(noner))
print(nr == er and er == noner and noner == nr)-
importallows you to bring in other peoples code- Python code resides in a module
-
randommodule allows for easy generation of random numbers- Specifically using it's
randint()function- Note that
randint()is inclusive on the upper bound unlikerange()- There is also
randrange()which matchesrange()'s parameters
- There is also
- Note that
- Specifically using it's
-
The
mathmodule- What can it do?
- Explore the Python standard library on the website!
- What can it do?
-
Functions that call themselves
-
Infinite example:
def message():
print("Hello there!")
message()
message()- Try to trace through that, lol
-
Base case, when you've the point that you should stop recursing
-
Recursive case, when you realize you need to make another recursive call
-
Write a recursive version of:
for i in range(5):
print(i)- ANS:
def rec_print(i):
i -= 1
if i < 0:
# base case
return
else:
# recursive case
rec_print(i)
print(i)
return
rec_print(5)- A simpler example:
def message(times):
if times > 0:
# recursive case
print("Hello there!")
message(times - 1)
# implicit base case
message(5)- How about computing a factorial?
def fact(n):
if n == 0:
# base case
return 1
else:
# recurisve case
return n * fact(n - 1)
print(fact(5))-
Trace this call stack
-
What about a recursive alrgorithm to multiply two numbers?
- Assuming the numbers are positive, nonzero ints
def mult(x, y):
if y > 0:
# recursive case
return x + mult(x, y - 1)
else:
# base case
return 0
print(mult(3, 4))- Discuss approaches to teaching recursion
- E.g., Illustrate call stack on the board
- Files will be stored on the hard drive
- In order to access their contents, you need to open them
cur_file = open("filename.txt")- This will allow you to read data out of filename.txt via
cur_file
- The first argument to
open()is a string that represents a path to a file - Hard drives are typically organized of heirarchies of files and folders
- Files contain data
- Folders contain files and other folders
- (Ignoring partitions...)
- The folder at the top of the heirarchy is the root of the file system
- Contains (directly or indirectly) all other files/folders on the drive
- On Linux/macOS the root is simply
/ - On Windows the root is something like
C:\- Windows has 1 root per drive
- Linux/macOS have 1 root period
- A string listing the series of folders you would need to take to get from the root to the file in question is called a path
- When a path is given that does not start at the root, it is treated as a relative path
- Picks up from the current directory
- Hence, when we said to open "filename.txt", we were asking to open the "filename.txt" in the current working directory
- Could be multiple different "filename.txt"'s on the same drive in different folders
contents = cur_file.read()- A bit inconvenient to read the entire file into a single variable...
cur_line = cur_file.readline()
while cur_line != "":
# process cur_line
cur_line = cur_file.readline()-
Whenever there are no more lines in a file, it will return an empty string
- How will we identify blank lines?
- Will still have
"\n"newline character!- Remember ASCII?
- Will still have
- How will we identify blank lines?
-
Potential for easily creating infinite loops...
for line in cur_file:
print(line)- Note that we couldn't actually run these one after another
- After the call to
cur_file.read(), there would be no more of the file to read! - One option is to use
seek()to get back to the beginning of the file:
- After the call to
cur_file = open("filename.txt")
contents = cur_file.read()
cur_file.seek(0)
for line in cur_file:
print(line)- Or we could re-open it
- But then we'd need to close it first...
cur_file = open("filename.txt")
contents = cur_file.read()
cur_file.close()
cur_file = open("filename.txt")
for line in cur_file:
print(line)- Good practice to always close a file you open
- (Though Python is pretty forgiving and will generally close files for you)
- Frees up resources dedicated to that file
- Held by the Python interpreter and the OS
- Great! Something else to forget!
with open("filename.txt") as cur_file:
for line in cur_file:
print(line)- Will automatically close the file when you exit the
withblock! withis much more powerful than just this, but that's all we'll cover here
- Remember the
"\n"will be a part of the line read in - And,
print()by default adds a newline character - Let's remove leading/trailing whitespace when we read in a line!
str.strip()
- Explore other string methods in Python
-
So that allows us to read, what about to write?
-
Have to open the file with a different mode
"r"read- Default if no mode is specified
"w"write- A new file will be created if one doesn't exist
- If one does exist, its contents will be erased
"a"append- Writes will be appended to the end of the file
- A new file will be created if one doesn't exist
"r+"read and write
-
Now, we can make use of
write()
with open("filename.txt", "a") as cur_file:
cur_file.write("A new line!\n")- All of these examples assumed that we were working with text files
- I.e., files formatted using the rules of some encoding like ASCII
- Hence how Python knows how a line ends
- I.e., files formatted using the rules of some encoding like ASCII
- We could also read binary files
- Don't assume any encoding rules, just treat it as a giant stream of bits
- Broken up in to 8-bit chunks, bytes
- Don't assume any encoding rules, just treat it as a giant stream of bits
- Can open a file in binary mode with a different mode spec:
"rb""wb""ab""rb+"
- Could open and process image files by opening them as binary files
- Where can this go wrong:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
print(x, "divided by", y, "is", x / y)- Check to make sure the user didn't enter a 0:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
if y == 0:
print("Can't divide by 0!")
else:
print(x, "divided by", y, "is", x / y)x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
try:
print(x, "divided by", y, "is", x / y)
except:
print("Can't divide by 0!")- Different handling for different errors:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
try:
print(x, "divided by", y, "is", x / y)
except ZeroDivisionError:
print("Can't divide by 0!")
except:
print("What have you done now?!")- Getting the details of the exception:
x = int(input("Enter a number: "))
y = int(input("Enter a number: "))
try:
print(x, "divided by", y, "is", x / y)
except ZeroDivisionError as err:
print("Can't divide by 0!")
print(err)
except Exception as err:
print("What have you done now?!")
print(err)- If no
except's are tripped, run theelseblock
try:
with open("numbers.txt") as infile:
total = 0
for line in infile:
total += int(line.strip())
except Exception as err:
print(err)
else:
print("The total is", total)- Note that you can access
totalafter thetrystatement (back in global scope)- However, it is dangerous to define it initially in the
tryblock- Remove "numbers.txt" and note that the program crashes after hanlding the error trying to deal with total
- Define it before entering the try block if you'll be using it outside
- Else, exception could be encountered before its initialized!
- However, it is dangerous to define it initially in the
- Whether we trip an
exceptor theelse, afterwards runfinallyblock - Note that
elseisn't necessary, can havetry...except...finally
try:
with open("numbers.txt") as infile:
total = 0
for line in infile:
total += int(line.strip())
except Exception as err:
print(err)
else:
print("The total is", total)
finally:
print("DONE")- Note that we can also get in trouble if we reference
totalin thefinallyblock- Same reason as referencing it after the entire
try!
- Same reason as referencing it after the entire
-
Mutable sequences of values
-
NEED EXAMPLES OF ALL OF THESE
- CAN WING IT
-
Can be initialized with
[] -
Can be indexed
-
Can be sliced
-
Size is dynamic, not pre-set like Java arrays or C arrays
- More like Java ArrayList
-
Can contain data of mixed types
-
len()still applies here -
Has operators similar to strings:
+is a concatenation operator*is a repetition operator- No vector math/matrix math support
- For that, look to NumPy...
-
Key list methods:
list.append(x): addxto the end of the listlist.remove(x): remove the first item from the list whose value isxa = [1, 2, 3] a.remove(2) a == [1, 3] #True
list.pop(): remove the last item in the listlist.pop(i): remove the item at indexia = [1, 2, 3] a.remove(1) a == [1, 3] #True
list.clear(): remove all items from the list
- Note this is outside the scope of Gaddis...
- Consider creating a list of the squares of numbers 0 - 9:
sq = []
for x in range(10):
sq = x ** 2- List comprehensions offer a shortcut syntax for things like this:
sq = [x ** 2 for x in range(10)]- Also supports an
ifclause:
odd_sq = [x ** 2 for x in range(10) if x % 2 != 0]- Can support multiple
forclauses:
t = [[x, y] for x in range(3) for y in range(3)]- Also note that by making a list of lists, we've covered Pythons approach to 2D arrays
t = [[x, y, z] for x in range(3) for y in range(3) for z in range(3) if x != y and x != z]- Can be nested:
t = [[str(x) + y for y in ['a', 'b', 'c']] for x in range(3)]- Write a recursive method to see if a given value is in a list (taking in a list and a value to search for):
def find(l, v):
if len(l) == 0:
return False
if l[0] == v:
return True
else:
return find(l[1:], v)- Now return the index that that value appears at and
-1if it isn't in the list:
def find(l, v):
if len(l) == 0:
return -1
if l[0] == v:
return 0
else:
rv = find(l[1:], v)
if rv == -1:
return rv
else:
return rv + 1- How many recursive calls do we have to make?
len(l)in the worst case- Each recursive call reduces the number of indices left to check by 1
- Can we do it in fewer?
- What about if we know the list is sorted?
- Can cut the number of indices left to check in half
- Check the middle
- Recurse left or right accordingly
- What about if we know the list is sorted?
def bin_search(l, v):
return bs_rec(l, v, 0, len(l))
def bs_rec(l, v, low, hi):
if v > l[hi - 1] or v < l[low]:
return -1
mid = (hi + low) // 2
if v == l[mid]:
return mid
elif v < l[mid]:
return bs_rec(l, v, low, mid)
elif v > l[mid]:
return bs_rec(l, v, mid + 1, hi)
else:
return -1-
How many recursive calls needed here?
- Or: how many times can we divide the list in half before there is only 1 index left to check?
- log2(n)
- Where n is the length of the original list
-
Would do Towers of Hanoi here
- Book version uses only ints
- Might be more intuitive to
append()andpop()from lists to add/remove the "disks" from the "pegs"
-
IMmutable sequences of values
-
Can be initialized with
()- or without...
-
Can be indexed
-
Can be sliced
-
Cannot update an item in the tuple
t = (1, 2, 3)
t[1] = 4 # ERROR-
Size cannot be changed (again, immutable!)
-
Has operators similar to strings/lists:
+is a concatenation operator*is a repetition operator- How are these allowed, I thought tuples were immutable?
- They are, we're not modifying the tuple, but producing a new tuple
3 + 5does not change the value of3or5, for example
-
How do you define an empty tuple?
()
- What about a tuple with only 1 element?
(1)will be interpreted as using the paren operators in a math statement- The commas in tuples of more than 1 item disambuguate the syntax
- But how do we define a singleton tuple?
(1,)-
Trailing comma disambiguates meaning
-
Actually, since the commas indicate a tuple, we can omit the parens:
t = 1, 2, 3
type(t) #<class 'tuple'>- Note this means the following is not a syntax error!
t = 1,
print(t) #(1,)-
This is referred to as tuple packing
-
Tuples can also be unpacked
t = 1, 2, 3
a, b, c = t
print(a)
print(b)
print(c)- Note that Python's ability to return multiple values is just tuple packing/unpacking
def test()
return 1, 2, 3 # packed into a tuple, return type is tuple
a, b, c = test() # returned tuple is unpacked-
Key/value stores
-
Initialization:
d = {"key1":"value1", "key2":"value2"}-
Can be indexed
- By keys
-
Cannot be sliced
-
Does NOT have
+and*operators like strings/lists/tuples -
{}initializes an empty dictionary
- Work similarly to list comprehensions:
dc = {x : x ** 2 for x in range(10)}-
Unordered collections with no duplicate elements
-
Cannot be indexed
-
Cannot be sliced
-
No
+or* -
Initialized with
{}
s = {1, 2, 3, 3, 3, 4}- Lack of key/value
:'s disambiguates from dictionaries- But how do you define an empty set??
s = set()
print(s) # set()
s.add(1)
print(s) # {1}set.isdisjoint(other): True if the intersection ofsetandotheris the empty setset.issubset(other): True ifsetis a subset ofother- Note that
<=can also be used! - And
<can be use for proper subset testing
- Note that
set.issuperset(other)- Note also have
<=and<
- Note also have
set.union(*others)|
set.intersection(*others)&
set.difference(*others)-
set.symmetric_difference(other): Return a new set with the items in either but not in both
- Like
set(), we also havelist(),tuple(), anddict()- Note that
dict()is a little tricky:
- Note that
d = dict(one=1, two=2, three=3)
e = dict([("one", 1), ("two", 2), ("three", 3)])
f = {'one': 1, 'two': 2, 'three': 3}
d == e == f # True- Can be used to convert between different collection types:
a = [1, 2, 3]
b = [3, 4, 5]
intersection = list(set(a) & set(b))
print(intersection) # [3]- Can also be used to covert other things to a collection:
r = range(10)
type(r) # <class 'range'>
l = list(r)
type(l) # <class 'list'>
print(l) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]- Procedural
- What we have been doing so far, program is built up using different procedures or functions
- Functional
- Makes extensive use of recursion
- Built out of small, pure functions
- Object oriented
- Currently the most popular
- Centers around breaking your program up and splitting it amongst different objects
- Contain both data (attributes) and code (methods)
- Note the distinction of method vs. function
- Former tied to object and operates on the object's data attributes
- Latter object independent
- Note the distinction of method vs. function
- By moving data into objects, we can achieve encapsulation
- Make it so that data attributes are accessible only via object methods
- Accessors: methods only to get attribute values
- Mutators: methods only to set attribute values
- Note that we can't actually do strict encapsulation in Python...
- Make it so that data attributes are accessible only via object methods
-
The blueprint for an object
-
Defining your own complex data type
-
All of the code used by an object in defined within a class
-
We create objects by instantiating classes
-
Can have multiple instances (objects) of the same class
-
Constructors
__init__()
-
Other special methods:
__str__()returns a string representation of the objectprint(x)is equivalent toprint(str(x))- Essentially
toString()from Java
__repr__()returns a representation of the object- repr() is implicitly called on items displayed by the Python interpreter
-
All methods defined with
selfas the first parameter to the class- Is implicitly passed in calling, however
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display(self):
print("Name:", self.name)
print("Age:", self.age)
print()
def __str__(self):
return self.name
def __repr__(self):
return "Person('" + self.name + "', " + str(self.age) + ")"
a = Person("Alice", 30)
print(a)
print(repr(a))
print(type(a))
a.display()- "Private" attributes in Python:
class Test:
def __init__(self):
self.a = "public"
self._a = "private by convention"
self.__a = "class-private"
t = Test()
print(t.a)
print(t._a)
print(t.__a) # Error
print(t._Test__a)
t.a = "modified!"
t._a = "modified, too!"
t.__a = "this will not error" # Creates new attribute
t._Test__a = "modified as well!"
print(t.a)
print(t._a)
print(t._Test__a)- Class variables:
- Defined outside of a method
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
f = Dog("Fido")
b = Dog("Buddy")
f.add_trick("roll over")
b.add_trick("play dead")
print(f.tricks)- Note that attributes can be added outside of
__init__()and futher outside of the class!- Empty classes can be used to just store data
class Empty:
pass
e = Empty()
e.test = 1
e.other_test = "another test!"
print(e)
print(repr(e))
print(type(e))
print(e.test)
print(e.other_test)-
Chains of "is-a" relationships:
- A student is a person
- student: subclass
- person: superclass
- A dog is a mammal; a mammal is an animal
- A mammal should have all the properties of an animal and then some
- A dog should have all the properties of a mammal and then some
- A student is a person
-
Two approaches to developing chains of inheritance:
- Specialization:
- Top-down approach
- Start with general classification
- E.g., all people
- Break into smaller groups based on group-specific attributes
- E.g., students
- Generalization:
- Bottom-up approach
- Start with individual groups
- E.g., dog, cat
- Identify shared attributes to define superclasses
- E.g., mammals, animals
- Specialization:
-
Polymorphism
- Allowing for different method definitions in super and sub classes
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display(self):
print("Name:", self.name)
print("Age:", self.age)
print()
def __str__(self):
return self.name
def __repr__(self):
return "Person('" + self.name + "', " + str(self.age) + ")"
class Student(Person):
def __init__(self, name, age):
super().__init__(name, age)
self.classes = []
def add_class(self, new):
self.classes.append(new)
def display(self):
super().display()
print("Classes:", self.classes)
print()
def __repr__(self):
return "Student('" + self.name + "', " + str(self.age) + ", " + str(self.classes) + ")"
def display_person(p):
p.display()
a = Person("Alice", 30)
print(a)
print(repr(a))
print(type(a))
display_person(a)
b = Student("Bob", 20)
print(b)
print(repr(b))
print(type(b))
display_person(b)
b.add_class("CS1520")
print(b)
display_person(b)- Multiple inheritance
- Python allows it
- Java does not
- Allows for lattice of inheritance instead of strict heirarchy
- A student-worker is a student and is a worker
- Needs all properties of both
- A student-worker is a student and is a worker
-
Any Python file is a module that can be imported into other Python modules with import
-
Let
a.pycontain:
def print_n():
for i in range(10):
print(i)
def print_l():
for l in ["a", "b", "c"]:
print(l)- Can then (in other files):
import a
a.print_n()from a import print_l
print_l()- Consider:
-
Running python
a.pyfrom the command line -
Having
import ain another Python file -
How can we have the former produce output while still being able to use the latter to pull in definitions??
-
Both will evaluate each line of a.py
- However,
python a.pywill have__name__set to"__main__" - Hence, we can choose what to do if run as a script:
- At the end of
a.py, have:
- At the end of
- However,
-
if __name__ == "__main__":
print("Producing output!")- Revisiting Person/Student example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display(self):
print("Name:", self.name)
print("Age:", self.age)
print()
def __str__(self):
return self.name
def __repr__(self):
return "Person('" + self.name + "', " + str(self.age) + ")"
class Student(Person):
def __init__(self, name, age):
super().__init__(name, age)
self.classes = []
def add_class(self, new):
self.classes.append(new)
def display(self):
super().display()
print("Classes:", self.classes)
print()
def __repr__(self):
return "Student('" + self.name + "', " + str(self.age) + ", " + str(self.classes) + ")"
def display_person(p):
p.display()
def main():
a = Person("Alice", 30)
print(a)
print(repr(a))
print(type(a))
display_person(a)
b = Student("Bob", 20)
print(b)
print(repr(b))
print(type(b))
display_person(b)
b.add_class("CS1520")
print(b)
display_person(b)
if __name__ == "__main__":
main()- Writing out a representation of objects to a file to reload their state later
- Can use pickle!
import pickle
l = [1, 2, 3, 4, 5]
with open("test.pkl", "wb") as outf:
pickle.dump(l, outf)
m = None
with open("test.pkl", "rb") as inf:
m = pickle.load(inf)
print(m)-
Lists, range objects, etc. are iterable
- Meaning that an iterator can be created for either type
-
Iterators must implement the method
__next__()- Successive calls to
__next__()will iterate through the items in the collection - When no more items remain in the collection, all future calls to
__next__()should raise aStopIterationexception
- Successive calls to
-
Iterators can be created from iterable items via the
iter()function -
Example
forloop:
for item in [1, 2, 3, 4, 5]:
print(item)temp_iter = iter([1, 2, 3, 4, 5])
while True:
try:
item = temp_iter.__next__()
except StopIteration:
break
print(item)- Functions that use the yield keyword to return values in order to create iterators
- State is maintained between function calls to the generator
def enum(seq):
n = 0
for i in seq:
yield n, i
n += 1
for f in enum(["apple", "orange", "banana"]):
print(f)def fibonacci():
i = j = 1
while True:
r, i, j = i, j, i + j
yield r
for fib in fibonacci():
print(fib)
if fib > 100:
break- Recall list/dictionary comprehensions from last time. What do you think this will produce into
x?
a = (x ** 2 for x in range(10) if x % 2 == 0)- NOT a tuple comprehension
- A generator expression
- Will yeild each of the items that would be placed into a list for the equivalent list comprehension
b = [x ** 2 for x in range(10) if x % 2 == 0]
for i in a:
print(i)
for j in b:
print(j)-
Why
for i in a:and notfor i in a():?- Because a is already a generator
- For
fibonacci, above, that function returns a generator
-
Why generator expressions when you already have list comprehensions?
- Memory footprint!
a = (x ** 2 for x in range(10000))
b = [x ** 2 for x in range(10000)]
import sys
sys.getsizeof(a)
sys.getsizeof(b)- How much space would a list of the
fibonnacigenerator take up?- Let's find out!
def fibonacci():
i = j = 1
while True:
r, i, j = i, j, i + j
yield r
sys.getsizeof(list(fibonnaci()))- Small, anonymous functions
a = lambda x: x ** 2
print(a(2))
print(a(4))
b = lambda x, y: x + y
print(b(2, 3))
print(b(4, 5))- The following are equivalent:
lambda PARAMS: EXPRdef <lambda>(PARAMS):
return EXPR- Can be used to easily create functions that return other functions:
def decrementer(n):
return lambda x: x-n
by5 = decrementer(5)
print(by5(5))
print(by5(2))
print(by5(7))
by3 = decrementer(3)
print(by3(5))
print(by3(2))
print(by3(7))- y tho?
- Consider
sorted()function - Takes a
keykeyword argument that is a function - The following will sort a list of integers by magnitude:
- Consider
l = [1, 5, 2, 7, -2, -20, -3, -8]
by_mag = sorted(l, key=lambda x: abs(x))
print(by_mag)print()accepts an arbitrary number of arguments, combines them together, and outputs them- How do you define a function that takes in an unknown number of inputs??
- Arbitrary argument lists
def foo(*args):
print(args)
foo(1, 2, 3, 4, 5)
foo("a", "string", 5, 8, "1")
foo("a", "string", 5, 8, "1", key="key") # TypeError, unexpected keyword argumentprint()also takes keyword arguments (e.g.,sepandend), though...
def faux_print(*args, sep=" ", end="\n"):
rv = str(args[0])
for a in args[1:]:
rv = rv + sep + a
rv += end
print("Would print '" + rv + "'")
faux_print("hello", "there", "testing")- Can you have arbitrary keyword aruments?
- Of course!
def bar(**kwargs):
print(kwargs)
bar(a="a", b="b", c=3)
bar(5, a="a", b="b", c=3) # TypeError, bar() takes 0 positional arguments but 1 was given- And they can be combined...
def baz(*args, **kwargs):
print(args)
print(kwargs)
baz(1, 2, 3, a="a", b="b", c="c")- Can use similar syntax to expand a list or a tuple into positional arguments
def check(a, b, c):
print(a)
print(b)
print(c)
print()
l = [1, 2, 3]
check(*l)- Or a dictionary into keyword arguments
d = {"b":2, "a":1, "c":3}
check(**d)- Refer to Gaddis 7.10 for line graph, bar chart, and pie chart examples
- Useful to help students organize their thoughts/develop algorithms independent of learning Python syntax
- Allows them to foster computational thinking without memorizing sytax
- Python turtle graphics
- Would be great for teaching elementary school programming
- Would be great for students that want to build a graphic calcualtor
- Other than that, meh
- Still just programming for the sake of programming
- Matplotlib examples
- Which we covered here
- GUI Programming subsection with the Canvas widget
- New appendices
- E: using the
importstatement - F: using
pipto install new packages
- E: using the
- New programming problems throughout
- 0401: Intermediate programming in Java
- Also offered through CHS!
- More advanced OO
- More advanced programming techniques overall
- 0441: Discrete structures
- Logic
- Counting
- Probability
- Relations
- CS-based math background
- 0445: Data structures
- How to implement a linked-list
- How is it structurally different from an array?
- Trees
- Heaps
- MUCH more advanced OO
- Last of the core programming skills
- How to implement a linked-list
- 0447: Intro to architecture
- How does the CPU perform its "tricks"?
- Overview of assembly
- Basic system architecture
- Should be able to (for a basic RISC architecture) "read" a binary instruction
- 0449: Intro to system programming
- Learn C
- Basics of interacting with an operating system
- Threading
- 1501: Algorithm implementation
- Tries/advanced searching techniques
- Graph algorithms
- Final test of programming skills
- 1502: Formal methods in CS
- Proofs and further logic
- Finite state machines
- 1550: Operating systems
- From here, electives!