Last week we learned about making functions -- now let's see some really great ones built into Python!
mystring = "Mixed Case"
print(mystring)
print(mystring.upper())
print(mystring.lower())
Why is this useful? You don't have to care about case...
userinput = "ExiT"
keyword = "exit"
print(userinput == keyword)
print(userinput.lower() == keyword)
Another really useful ability: stripping whitespace at the beginning/end of a string.
This comes in handy when you ask someone to type a response, as well as (next week) when processing files.
userinput = " ExiT " # e.g., via input("Enter Command: ")
keyword = "exit"
print(userinput == keyword)
print(userinput.strip().lower() == keyword)
Lastly, it is possible to compare strings.
For the most part this does what you would expect (alphabetical), but note that case matters (and uppercase is "less than" lowercase).
print("a" < "b")
print("Alligator" < "Armory")
print("A" < "b")
print("a" < "B")
We already saw how to add to lists, what other things can we do?
mylist = [1, 2, 3]
# Functions that make sense for lists of numbers
print(sum(mylist)) # add all the elements
print(min(mylist)) # find the smallest value
print(max(mylist)) # find the biggest value
When sorting and reversing lists there are two version: one that changes a list, the other produces a new list.
mylist = [1, 2, 3]
print(mylist)
print(list(reversed(mylist))) # doesn't change mylist
print(mylist)
print()
mylist.reverse() # changes mylist (doesn't return anything)
print(mylist)
mylist = [8, 6, 7, 5, 3, 0, 9]
print(mylist)
print(list(sorted(mylist))) # doesn't change mylist
print(mylist)
print()
mylist.sort() # changes mylist (doesn't return anything)
print(mylist)
Now let's remove elements from a list via the pop
function, which you supply an index.
mylist = [1, 2, 3, 4, 5]
print(mylist)
removed = mylist.pop(2)
print(removed)
print(mylist)
Importantly, when you provide a list to a function, that function can modify the supplied list...
def pop_first(l):
if len(l) > 0:
return l.pop(0) # changes the list, returns the first element (now removed)
mylist = [1, 2, 3, 4, 5]
print(mylist)
print(pop_first(mylist))
print(mylist)
Lastly, we've seen a pattern over and over with functions on lists...
def my_list_func(mylist, check_func, element_func):
result = []
for element in mylist:
if check_func(element):
result.append(element_func(element))
return result
def double(x):
return 2 * x
def nothing(x):
return x
def yes(x):
return True
def is_even(x):
return x % 2 == 0
mylist = [1, 2, 3, 4, 5]
# Example: double all values
print(my_list_func(mylist, yes, double))
# Example: keep only even
print(my_list_func(mylist, is_even, nothing))
# Example: double only even
print(my_list_func(mylist, is_even, double))
A fast way to do this is a list comprehension...
[element_func(x) for x in mylist if check_func(x)]
Note: if check_func(x)
is optional
def double(x):
return 2 * x
def nothing(x):
return x
def yes(x):
return True
def is_even(x):
return x % 2 == 0
mylist = [1, 2, 3, 4, 5]
# Example: double all values
print([2*x for x in mylist])
# print([double(x) for x in mylist]) # either is fine
# Example: keep only even
print([x for x in mylist if x % 2 == 0])
# print([x for x in mylist if is_even(x)]) # either is fine
# Example: double only even
print([2*x for x in mylist if x % 2 == 0]) # or any combination from above
We've already done plenty of indexing on strings/lists/tuples, now let's add some powers!
Start with negative indexing, meaning counting from the back...
mylist = [1, 2, 3]
mystring = "abc"
print(mylist[-1])
print(mylist[-2])
print(mystring[-1])
print(mystring[-2])
Even more interesting, you can "slice" a list/string using a combination of indexing syntax ([]
) and what we learned for the range
function: var[start:end:step]
(where all are optional; missing first=beginning, missing second=end, missing last=1)
mylist = [1, 2, 3, 4, 5]
mystring = "abcde"
print(mylist[1:3]) # second and third elements
print(mylist[2:]) # start with the third, go till the end
print(mylist[-2:]) # start one from the back, go till the end
print(mystring[::2]) # start at the beginning, go till the end, step by 2
print(mylist[:]) # trick for "cloning" (making a copy)
print(mystring[::-1]) # trick for reversing :)
Functions allow us to compartmentalize code, modules allow us to compartmentalize functions, variables, and other things we'll learn about later.
To gain access to a module, use import module_name
and then you have access to module_name.whatever
...
import string
print(string.ascii_lowercase)
print(string.ascii_uppercase)
print(string.digits)
print(string.punctuation)
There are a couple variations with importing to know about...
from string import ascii_lowercase, ascii_uppercase # just get lower/uppercase, no need to string.
print(ascii_lowercase)
print(ascii_uppercase)
import string as silly_name # rename the module
print(silly_name.digits)
from string import punctuation as silly_punctuation # rename the variable
print(silly_punctuation)
from string import * # get everything, no need to string.
print(digits)
Some basic modules to know about...
import math
print(math.pi)
print(math.sqrt(289))
import statistics
print(statistics.mean([1, 6, 4, 2, 5]))
print(statistics.median([1, 6, 4, 2, 5]))
print(statistics.stdev([1, 6, 4, 2, 5]))
import random
my_seed = 42
random.seed(my_seed) # allows you to reproduce a sequence of "random" numbers
print(random.random()) # 0-1
print(random.random()) # 0-1
print(random.random()) # 0-1
random.seed(my_seed)
print(random.random())
print(random.random())
print(random.random())
print()
for i in range(20):
flip = random.randint(0, 1) # flip a coin (0=heads, 1=tails)
if flip == 0:
print("heads")
else:
print("tails")
When using/creating functions, it is common to encounter/define default parameters -- meaning they don't have to be supplied and, if they aren't, they have a known value.
def myfunc(a, b=5):
return a + b
print(myfunc(1, 2))
print(myfunc(1))
# Other examples we've already seen...
print(list(range(10)))
print(list(range(5, 10)))
print("hi,how,are,you".split())
print("hi,how,are,you".split(','))
Once there are several optional parameters, it becomes easier to provide named arguments...
def myfunc(a, b=5, c=7, d=11):
return sum((a, b, c, d))
print(myfunc(1))
print(myfunc(1, d=10))
print(myfunc(a=1, b=2, c=3, d=4))
When code is imported, it's not a great idea to execute code (only define functions/variables/etc that can be used by the importing code). SO, when writing code that can be executed (via python myapp.py
, it's common to protect it via the following if
statement...
def main():
print('Code to be executed when running via python (but not import)')
if __name__ == "__main__":
main()