Sunday, July 3, 2022

Python - File handling

# Read a file

try:
    f = open("demofile.txt")
    f.close()
    print("Accessed file OK")
except FileNotFoundError:
    print("File does not exist")
finally:
    print("All done.")

"""

Syntax:
open("filename", "mode")

Default mode values:
r = read
t = text
    
These are the same
f = open("demofile.txt")
f = open("demofile.txt", "rt")

Open Modes:
r = read
a = append = Open for appending, creates if file does not exist
w = write = Open for writing, creates if file does not exist
x = create = Creates the file for writing, returns error if already exists

Handling modes:
t = text
b = binary

"""


# Open, read, and display a file
f = open("c:\\temp\\demofile.txt", "r")
print(f.read())
f.close()

# Open and read 5 characters
f = open("demofile.txt", "r")
# "This "
print(f.read(5))
f.close()


# Open and read a line
f = open("demofile.txt", "r")
# "This is"
print(f.readline())
f.close()

# Open and read two lines
f = open("demofile.txt", "r")
# "This is"
print(f.readline())
print(f.readline())
f.close()

# Open and loop through each line of the file
f = open("demofile.txt", "r")
for x in f:
    print(x)
f.close

# Append to a file
f = open("demofile2.txt", "a")
f.write("Now the file has more content!")
f.close


# Open and loop through each line of the file
f = open("demofile2.txt", "r")
for x in f:
    print(x)
f.close

# Overwrite a file if it exists
f = open("demofile3.txt", "w")
f.write("Woops! I have deleted the content!")
f.close()

# Delete a file
import os
os.remove("demofile2.txt")

# If the file exists, delete it
if os.path.exists("demofile3.txt"):
    os.remove("demofile3.txt")
else:
    print("The file does not exist")



   

Saturday, July 2, 2022

Python - String formatting

price = 49
txt = "The price is {} dollars"
# The price is 49 dollars
print(txt.format(price))

name = "Fred"

txt = "The price is {} dollars, {}."
# The price is 49 dollars, Fred.
print(txt.format(price, name))

txt = "The price is {:.2f} dollars"
#The price is 49.00 dollars
print(txt.format(price))

quantity = 3
itemno = 567
price = 49
myorder = "I want {0} pieces of item number {1} for {2:.2f} dollars."
# I want 3 pieces of item number 567 for 49.00 dollars.
print(myorder.format(quantity, itemno, price))

myorder = "I have a {carname}, it is a {model}."
# I have a Ford, it is a Mustang.
print(myorder.format(carname = "Ford", model = "Mustang"))


Python - User input

# User input for a string
username = input("Enter username:")

print("Hello, " + username)

      
# Note that this only gets a string.

# It does not scan for key presses



Python - Error handling (try, except, else, finally)

x = "hello world"


try:
    print(x)
    # This will cause an error
    #y = 3 / 0
except NameError:
    # Catch a specific error type
    print("Forgot to define x")
except:
    # Catch all other errors
    print("An exception occured")
else:
    # Run this if there was no error
    print("All is well")
finally:
    # Do this regardless of error status
    print("That's all folks")

    
    
# Throw an error on purpose
x = -1

if x < 0:
    raise Exception("Sorry, no numbers below zero")




Python - Regular Expressions

# Import the regular expression module
import re

txt = "The rain in Spain"

# This searches for:
# ^The = "The" at the beginning of the line (^)
# Spain$ = "Spain" at the end of the line ($)
x = re.search("^The.*Spain$", txt)

print(x)

# Pretty much any populated variable evaluates to True
# So if we got a match, that value is tucked into x
# Which means this will evaluate to True if we got a match
if bool(x) == True:
    print("We got a match")

else:
    print("We did not geta match")

# Split returns a list where the string has been split at each match
# So the next line returns all the words separated by a space:
# The, rain, in, Spain
words = re.split(" ", txt)
print(words)

# Sub replaces one or many matches with a string
new = re.sub("Spain", "England", txt)
# The rain in England
print(new)

# Review regular expression matching
# A quick review sheet is here:
# https://www.w3schools.com/python/python_regex.asp


Python - Math

# Built-in math functions

myValues = (5, 10, 25)

# minimum, maximum
x = min(myValues)
y = max(myValues)

# 5, 25
print(x)
print(y)

# Absolute value
x = abs(-7.25)
# 7.25
print(x)

# x to the power of y
z = pow(4,3)
# 64
print(z)

print("----")


# Imported math functions
import math

x = math.sqrt(64)
print(x)

# Ceiling = round up to nearest integer
# Floor = round down to nearest integer

x = math.ceil(1.4)
y = math.floor(1.4)

# 2, 1
print(x)
print(y)

# PI is a constant (3.14....)
x = math.pi
# 3.14....
print(x)



Python - Dates

import datetime

x = datetime.datetime.now()
# 2022-07-02 18:23:57.824673
print(x)

# The datetime module has many methods, here are some

# 2022
print(x.year)

# Saturday
print(x.strftime("%A"))

# Create a date object

x = datetime.datetime(2020, 5, 17)
# 2020-05-17 00:00:00
print(x)

# Format date objects into readable strings via strftime()
# Good listing at w3schools:
# https://www.w3schools.com/python/python_datetime.asp

x = datetime.datetime(2018, 6, 1)

# %B = Month name
# June
print(x.strftime("%B"))

# %Y = year
# 2018
print(x.strftime("%Y"))

# %m = month as a two digit number
# 06
print(x.strftime("%m"))

# %d = day as a two digit number
# 01
print(x.strftime("%d"))

# There's probably a better way to do it than this
myFormattedDate = x.strftime("%Y") + "-" \
    + x.strftime("%m") + "-" \
    + x.strftime("%d")

# 2018-06-01
print(myFormattedDate)

# This works the same way
myFormattedDate = x.strftime('%Y-%m-%d')
print(myFormattedDate)

# Today's date
# 2022-07-02 18:42:02.671457
print(datetime.datetime.today())

# 2022-07-02
print(datetime.datetime.today().strftime('%Y-%m-%d'))


Python - Modules

Modules are like libraries.

They let you tap into other people's code and packages.

For example, say there is a file named "mymodule.py" with these contents (between the dashed lines):

---------

def greeting(name):
    print("Hello, " + name)

---------

We would then access the module with the code below:

import mymodule

# Hello, John
mymodule.greeting("John")


Here is how to access variables stored in a module:

Filename: mymodule.py:

--------- 

person1 = {
  "name": "John",
  "age": 36,
  "country": "Norway"
}


---------

How I would use it:

import mymodule

a = mymodule.person1["age"]
 

#36
print(a)

There are built-in modules.  For example, platform

We can display a list of functions in a module like this:

import platform

x = dir(platform)
print(x)



Python - Scope

# Variables are scoped
# It is only available from inside region it is created

def myfunc():
  print("Inside function")
  x = 300
  print(x)
  print("Exiting function")

# 300
myfunc()
# Next line fails
#print(x)

print("----")

# This sequence sets and displays a variable
# It then calls a function which *looks* like it messes with same variable
# But it shows that when we exit the function,
# the variable is intact

x = 7
# 7
print(x)

# From inside function, we will see 300
myfunc()

# 7
print(x)

print("=======")

# This shows that outer variables can be accessed from inner regions

# Define a global variable
outer = "my outer variable"

def myfunc():
    inner = "my inner variable"
    # Works as expected - we can see both
    print(outer)
    print(inner)
    
myfunc()

print("<><><><><><><>")

# Use global keyword to declare a variable global from within an inner region

def myfunc():
    global surprise
    surprise = 777

myfunc()
# the surprise variable is now treated as a global
# 777
print(surprise)

print("<><><><><><><>")

# We can change global variables from within an inner region
# by using the global keyword

x = 300

def myfunc():
  global x
  x = 200

myfunc()

# 200
print(x)


Python - Classes and Inheritance

class MyClass:
  x = 5
 
p1 = MyClass()
# 5
print(p1.x)

print("---")

# built-in __init__() function is called on object instatiation
class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

p1 = Person("John", 36)

# John, 36
print(p1.name)
print(p1.age)

print("===")


class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def myfunc(self):
    print("Hello my name is " + self.name)

p1 = Person("John", 36)
# Hello my name is John
p1.myfunc()

#36
print(p1.age)

# Set property
p1.age = 40

# 40
print(p1.age)

# <class '__main__.Person'>
# So this tells us it is an object of type Person

print (type(p1))


# Delete an object
del p1

# A class with no definition
class Person:
    pass

print("******")

# Inheritance, Parents, Children

# First define a class

class Person:
  def __init__(self, fname, lname):
    self.firstname = fname
    self.lastname = lname

  def printname(self):
    print(self.firstname, self.lastname)

x = Person("John", "Doe")

# John Doe
x.printname()

# Now create a child of the parent

class Student(Person):
    pass

y = Student("Sarah", "Smith")
# Sarah Smith
y.printname()

print("------------")

# Notice that we never defined a method called printname() in the child class
# But the Student inherited the methods of the parent class (Person)

class Student(Person):
    def __init__(self, fname, lname):
        #add properties here
        pass
    
y = Student("Sarah", "Smith")

# This next line breaks because we over-rode the inheritance of the parent
# by defining our own __init__() function in the chil
# In other words, the child's __init__() function overrides the inheritance
# of the parent's __init()__ function
#y.printname()

# We fix it by adding a call to the parent's __init__() function

class Student(Person):
    def __init__(self, fname, lname):
        Person.__init__(self, fname, lname)


y = Student("Sarah", "Smith")

# Now this works
# Sarah Smith
y.printname()

print("------------")

# By using super() we automatically inherit the methods and properties from parent
class Student(Person):
  def __init__(self, fname, lname):
    super().__init__(fname, lname)
    self.graduationyear = 2019

    
y = Student("Sarah", "Smith")
# Sarah Smith
y.printname()
# 2019
print (y.graduationyear)

print("============")

# Make graduationyear a variable

class Student(Person):
  def __init__(self, fname, lname, year):
    super().__init__(fname, lname)
    self.graduationyear = year

y = Student("Sarah", "Smith", 2022)

# Sarah Smith
y.printname()
# 2022
print (y.graduationyear)

print("<><><><><><>")

# Add a method

class Student(Person):
  def __init__(self, fname, lname, year):
    super().__init__(fname, lname)
    self.graduationyear = year

  def welcome(self):
    print("Welcome", self.firstname, self.lastname, "to the class of", self.graduationyear)




Python - Functions

# Functions

def my_function():
  print("Hello from a function")

my_function()

# So apparently we can redefine functions
# Notice the function name below is the same one defined above
def my_function(fname):
    print(fname + " Smith")
    

my_function("John")
my_function("Jane")

# Two args
def my_function(fname, lname):
    print (fname + " " + lname)

my_function("John", "Doe")
my_function("Jane", "Smith")

# This will break because not right number of args
#my_function("Pat")
#my_function("Richard", "Paul", "Astley")

print ("---")

def my_function(*kids):
    for kid in kids:
        print(kid)
# Prints John, Jane, Richard    
my_function("John", "Jane", "Richard")

print ("---")

# You can specify arguments instead of relying on order
def my_function(child3, child2, child1):
  print("The youngest child is " + child3)

# Displays Linus
my_function(child1 = "Emil", child2 = "Tobias", child3 = "Linus")


print ("===")

# Default parameter values
def my_function(country = "Norway"):
  print("I am from " + country)

my_function("Sweden")
my_function("India")
# Displays "I am from Norway"
my_function()
my_function("Brazil")

# Return a value
def my_function(x):
    return 5 * x

# Displays 15, 25, 45
print(my_function(3))
print(my_function(5))
print(my_function(9))

print ("---")

# Empty functions
def myfunction():
    pass


Python - For loops

# Prints each item, one on each line
fruits = ["apple", "banana", "cherry"]
for x in fruits:
  print(x)
 
# Prints each letter in 'banana', one on each line
for x in "banana":
  print(x)

print ("---")

 
# Break exits the loop
# Displays apple, then banana, and then breaks out of the loop
# (So it never gets to cherry)
fruits = ["apple", "banana", "cherry"]
for x in fruits:
  print(x)
  if x == "banana":
    break

print ("---")

# This does the same thing but the break is before the print statement
# So we only see "apple"
# Recall that we are breaking out of the FOR loop, not just the IF condition

fruits = ["apple", "banana", "cherry"]
for x in fruits:
  if x == "banana":
    break
  print(x)


print ("===")

# continue says stop the current iteration of the loop
# and continue with the next one
# apple
# rotten!
# cherry

fruits = ["apple", "banana", "cherry"]
for x in fruits:
  if x == "banana":
    print("rotten!")
    continue
  print(x)

# Displays 0 through 5
for x in range(6):
  print(x)


print ("===")

# Displays 2 through 5
for x in range(2, 6):
  print(x)

print ("===")

# Weird
# Start at 2
# Keep going up to right before 30
# Increment by the third argument: 3
# So it displays 2, 5, 8, 11, 14, 17, 20, 23, 26, and 29
for x in range(2, 30, 3):
  print(x)
 
print("<><><><>")

# Displays 0 through 5 and then executes the else statement
# The else keyword says run this block of code when the loop statement is finished
for x in range(6):
  print(x)
else:
  print("Finally finished!")

print("<><><><>")

# Displays 0
for x in range(1):
  print(x)
 
print("<><><><>")

# Displays 0, 1
for x in range(2):
  print(x)

print("<><><><>")

# Displays 0, done
for x in range(1):
    print(x)
else:
    print("done")
    
print("<*><*><*><*>")

# Displays done
for x in range(0):
    print(x)
else:
    print("done")

# The break statement completely exits out of the for loop
# It will not execute the else block
# Displays 0, 1, 2
print ("----")
for x in range(6):
  if x == 3: break
  print(x)
else:
  print("Finally finished!")
 
# Fun with nested loops
# Displays red apple, red banana, red cherry, big x3, tasty x3
adj = ["red", "big", "tasty"]
fruits = ["apple", "banana", "cherry"]

for x in adj:
  for y in fruits:
    print(x, y)

# What you have to do if no content in a for loop    

for x in [0, 1, 2]:
  pass


Python - While loops

# While loop
# Displays 1 through 5
i = 1
while i < 6:
  print(i)
  i += 1

print("---")

# break statement
# Displays 1 through 3
i = 1
while i < 6:
  print(i)
  if i == 3:
    break
  i += 1
 
print("---")

# continue statement
# Displays 1 through 6
i = 0
while i < 6:
  i += 1
  if i == 3:
    continue
  print(i)

print("---------")

# Displays 1 through 5, and then executes the else statement
# While ... Else
i = 1
while i < 6:
  print(i)
  i += 1
else:
  print("i is no longer less than 6")

Python - If... Else

# Fun with if conditions

a = 33
b = 200
if b > a:
  print("b is greater than a")

a = 33
b = 33
if b > a:
  print("b is greater than a")
elif a == b:
  print("a and b are equal")

a = 200
b = 33
if b > a:
  print("b is greater than a")
elif a == b:
  print("a and b are equal")
else:
  print("a is greater than b")

a = 200
b = 33
if b > a:
  print("b is greater than a")
else:
  print("b is not greater than a")

print("---")

a = 200
b = 33

# Short Hand If
if a > b: print("a is greater than b")

# Short Hand If ... Else
a = 2
b = 330
print("A") if a > b else print("B")

print("-----")

# AND

a = 200
b = 33
c = 500
if a > b and c > a:
  print("Both conditions are True")

# Parentheses seems to work
if (a > b) and (c > a):
  print("Both conditions are True")
 
a = 200
b = 33
c = 500
if a > b or a > c:
  print("At least one of the conditions is True")

print("<><><><>")

x = 41

if x > 10:
  print("Above ten,")
  if x > 20:
    print("and also above 20!")
  else:
    print("but not above 20.")

# If statements cannot be empty but we can use pass
a = 33
b = 200

if b > a:
    # Do nothing
    # Python won't let me just have comments here
    # I have to use the pass statement
    pass


Python - Dictionaries

# Dictionaries

thisdict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# {'brand': 'Ford', 'model': 'Mustang', 'year': 1964}
print(thisdict)

# Ford
print(thisdict["brand"])

# 3
print(len(thisdict))

mymodel = thisdict["model"]

# Mustang
print (mymodel)

mykeys = thisdict.keys()

# dict_keys(['brand', 'model', 'year'])
print (mykeys)

myvalues = thisdict.values()
# dict_values(['Ford', 'Mustang', 1964])
print (myvalues)

myitems = thisdict.items()
# dict_items([('brand', 'Ford'), ('model', 'Mustang'), ('year', 1964)])
print (myitems)

print("---")

thisdict =    {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Change a value
thisdict["year"] = 2018
# 2018
print(thisdict["year"])


thisdict.update({"year": 2020})
# 2020
print(thisdict["year"])

# Add an item
thisdict["color"] = "red"
# {'brand': 'Ford', 'model': 'Mustang', 'year': 2020, 'color': 'red'}
print (thisdict)

# This updates the value
# If the value does not exist, it will add it
thisdict.update({"color": "blue"})
thisdict.update({"wheels": 4})
# {'brand': 'Ford', 'model': 'Mustang', 'year': 2020, 'color': 'blue', 'wheels': 4}
print (thisdict)


# Pop removes the last inserted item and returns the item
# 5
print(len(thisdict))
x = thisdict.popitem()
# ('wheels', 4)
print(x)
# 4
print(len(thisdict))


del thisdict["color"]
del thisdict["model"]
# {'brand': 'Ford', 'year': 2020}
print(thisdict)

# Clear the dictionary of all items
thisdict.clear()
# {}
print(thisdict)

# Remove the dictionary completely
del thisdict
# Next line will cause an error
#print(thisdict)

print("---")
thisdict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# This will loop through and show the keys
# brand
# model
# year
for x in thisdict:
    print(x)

# This will loop through and show the values
# Ford
# Mustang
# 1964
for x in thisdict:
    print(thisdict[x])

print("---")

# Ford
# Mustang
# 1964
for x in thisdict.values():
    print(x)

# brand
# model
# year
for x in thisdict.keys():
    print(x)

print("---")

# Gnarly - loop through both keys and values
# brand Ford
# model Mustang
# year 1964

for x, y in thisdict.items():
  print(x, y)

print("======")

 
# Copy a dictionary
# Truly a copy and not a reference
thisdict =    {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
mydict = thisdict.copy()
print(mydict)

print("---")

mydict["year"] = 2020

print(mydict)

print(thisdict)

print("---")

myfamily = {
  "child1" : {
    "name" : "Emil",
    "year" : 2004
  },
  "child2" : {
    "name" : "Tobias",
    "year" : 2007
  },
  "child3" : {
    "name" : "Linus",
    "year" : 2011
  }
}

# <class 'dict'>
print(type(myfamily))
# <class 'dict'>
print(type(myfamily["child1"]))

# {'name': 'Emil', 'year': 2004}
print(myfamily["child1"])

# Emil
print(myfamily["child1"]["name"])
# <class 'str'>
print(type(myfamily["child1"]["name"]))



Python - Set joins

# We can combine two sets with the union method
set1 = {"a", "b", "c"}
set2 = {1, 2, 3}

set3 = set1.union(set2)

# {'a', 1, 2, 3, 'c', 'b'}
print(set3)

# Add items from set2 to set1
set1.update(set2)
print (set1)

# Both union and update exclude duplicates

# intersection_update keeps only items that are present in both sets
x = {"apple", "banana", "cherry"}
y = {"google", "microsoft", "apple"}

x.intersection_update(y)
# {'apple'}
print(x)

# intersection() returns a NEW set of items present in both sets
x = {"apple", "banana", "cherry"}
y = {"google", "microsoft", "apple"}

z = x.intersection(y)
# {'apple'}
print(z)

# Keep all but NOT the duplicates
x = {"apple", "banana", "cherry"}
y = {"google", "microsoft", "apple"}

x.symmetric_difference_update(y)
# {'cherry', 'microsoft', 'google', 'banana'}
# Notice no 'apple'
print(x)


Python - Add and remove items from a set

# Sets are unordered, unchangeable, and unindexed
# Unchangeable means you cannot change the item in a set,
# but you CAN add or remove items from the set

# sets use curly brackets {}
myset = {"apple", "banana", "cherry"}

# No duplicates
myset = {"apple", "banana", "cherry", "apple"}
# {'apple', 'cherry', 'banana'}
print (myset)
# 3
print(len(myset))

# Loop through a set
for x in myset:
    print(x)

# Add item
myset.add("orange")
# {'orange', 'banana', 'cherry', 'apple'}
print(myset)

# Add items from another set to myset
tropical = {"pineapple", "mango", "papaya"}
myset.update(tropical)
# {'orange', 'apple', 'mango', 'papaya', 'banana', 'pineapple', 'cherry'}
print(myset)

myset.remove("orange")
# {'pineapple', 'cherry', 'mango', 'papaya', 'banana', 'apple'}
# (notice no 'orange')
print(myset)

# If you try to remove the item and it does not exist,
# Python will raise an error
# myset.remove("orange")

# However, the discard() method will NOT raise an error
myset.discard("pineapple")
print(myset)
myset.discard("pineapple")
print(myset)

# Pop removes the last item from the set
# Its return value is the item removed
x = myset.pop()
print(myset)

print("Notice that ", x , " no longer exists in the set: " , myset)


print("---")

# Clear removes all items from the set
myset.clear()
print(myset)
del myset
# This would raise an error because the set no longer exists
#print(myset)



Friday, July 1, 2022

Python - Unpacking Tuples

# Python can "unpack" tuple items into variables like this:

fruits = ("apple", "banana", "cherry")
(green, yellow, red) = fruits

# apple
print(green)
# banana
print(yellow)
# cherry
print(red)

print("---")

# This will cause an error because there are
# too many values to unpack
# into only three variables
fruits = ("apple", "banana", "cherry", "strawberry", "raspberry")
#(green, yellow, red) = fruits

# So use an asterisk * instead

(green, yellow, *red) = fruits

# apple
print(green)
# banana
print(yellow)
# ['cherry', 'strawberry', 'raspberry']
print(red)

# All the remaining variables got shoved into a list called "red"
# <class 'list'>
print(type(red))

(green, *tropic, red) = fruits

print("---")

# apple
print(green)
# You will notice that Python assigned all the values to "tropic"
# except the last one so it matched the number of variables we provided.
# ['banana', 'cherry', 'strawberry']
print(tropic)
# raspberry
print(red)


Python - Tuples

# Lists use a square bracket []
# Tuples use parentheses ()
mylist =  ["apple", "banana", "cherry", "apple"]
mytuple = ("apple", "banana", "cherry", "apple")

#<class 'list'>
#<class 'tuple'>
print (type(mylist))
print (type(mytuple))

# Length of tuple
# 4
print(len(mytuple))

# A tuple with only one item is a bit gnarly
# Notice the ending comma:
thistuple = ("apple",)
# If we forget the comma it is a string
myString = ("apple")

# <class 'str'>
print (type(myString))

# Since tuples are "unchangeable", we must jump thru some hoops
# if we *do* want to change one.
# Set up a tuple
x = ("apple", "banana", "cherry")
# Create a list type of the tuple
y = list(x)
# Make your change
y[1] = "kiwi"
# Now redefine the original tuple as a tuple
#   but give it the contents of the changed list
x = tuple(y)

print(x)

# Adding items to an unchangeable tuple is similar
thistuple = ("apple", "banana", "cherry")
y = list(thistuple)
y.append("orange")
thistuple = tuple(y)
print(thistuple)

print("---")

# Removing an item from an unchangeable tuple is similar
thistuple = ("apple", "banana", "cherry")
y = list(thistuple)
y.remove("apple")
thistuple = tuple(y)
print(thistuple)




Python - Join Lists

# Multiple ways to join two lists

# One way
list1 = ["a", "b", "c"]
list2 = [1, 2, 3]

list3 = list1 + list2
print(list3)

# Another way
list1 = ["a", "b" , "c"]
list2 = [1, 2, 3]

for x in list2:
  list1.append(x)

print(list1)

# A third way
list1 = ["a", "b" , "c"]
list2 = [1, 2, 3]

list1.extend(list2)
print(list1)




Python - Copy lists

list1 = ["apple", "banana", "cherry"]
list2 = list1
# Right now these are identical
print (list1)
print (list2)

list1.append('orange')

# Notice that the new item is also added to list2
# ['apple', 'banana', 'cherry', 'orange']
print (list1)
print (list2)

# I think this means list2 is a pointer referencing list1.
# So changes made to list1 are seen in list2,
# and vice-versa.

list2.pop()
# ['apple', 'banana', 'cherry']
print (list1)
print (list2)

# So we need a way to actually *copy* the list
list2 = list1.copy()

list1.append('orange')
list2.pop()

# Now we're cooking with gas
# ['apple', 'banana', 'cherry', 'orange']
# ['apple', 'banana']
print (list1)
print (list2)

print("---")
# Another way to copy a list
list1 = ["apple", "banana", "cherry"]
list3 = list(list1)

# ['apple', 'banana', 'cherry']
print (list3)
list3.insert(2, "popsicle")

#['apple', 'banana', 'cherry']
#['apple', 'banana', 'popsicle', 'cherry']
print (list1)
print (list3)



Python - Sort lists

# Sort a list
thislist = ["orange", "mango", "kiwi", "pineapple", "banana"]
thislist.sort()
# ['banana', 'kiwi', 'mango', 'orange', 'pineapple']
print(thislist)

# I guess Python knows to sort numerically instead of alphabetically
# if the items in the list are all numeric
thislist = [100, 50, 65, 82, 23]
thislist.sort()
# [23, 50, 65, 82, 100]
print(thislist)

# Sort descending
thislist = ["orange", "mango", "kiwi", "pineapple", "banana"]
thislist.sort(reverse = True)
# ['pineapple', 'orange', 'mango', 'kiwi', 'banana']
print(thislist)

# Sort numbers descending
thislist = [100, 50, 65, 82, 23]
thislist.sort(reverse = True)
# [100, 82, 65, 50, 23]
print(thislist)

# Sort is case sensitive
thislist = ["banana", "Orange", "Kiwi", "cherry"]
thislist.sort()
# ['Kiwi', 'Orange', 'banana', 'cherry']
print(thislist)

# How to do a case insensitive sort
thislist = ["banana", "Orange", "Kiwi", "cherry"]
thislist.sort(key = str.lower)
# ['banana', 'cherry', 'Kiwi', 'Orange']
print(thislist)

# This reverses the items in a list
thislist = ["banana", "Orange", "Kiwi", "cherry"]
thislist.reverse()
# ['cherry', 'Kiwi', 'Orange', 'banana']
print(thislist)


Python - List Comprehension

# List comprehension is weird
# It is a shortcut

# W3Schools gives an example where you have a list of fruits
# and want to return a new list containing only the fruits
# with the letter "a" in the name

# The typical way:

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = []

# This will loop through all the items in the list "fruits"
for x in fruits:
  # If the letter "a" is in the item
  if "a" in x:
    # Then add this item to the new list
    newlist.append(x)

# ['apple', 'banana', 'mango']
print(newlist)

# We can use list comprehension to do the same thing
# but with fewer lines of code

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = [x for x in fruits if "a" in x]

# ['apple', 'banana', 'mango']
print(newlist)

# You can also manipulate the items in the list before you return
# the result like this:
newlist = [x.upper() for x in fruits]

# ['APPLE', 'BANANA', 'CHERRY', 'KIWI', 'MANGO']
print(newlist)

Python - Loop through lists

# Loop through a list
thislist = ["apple", "banana", "cherry"]
# This will show each item in the list, one line per item
# apple
# banana
# cherry
for x in thislist:
  print(x)

# 3
print (len(thislist))

# range(0, 3)
print (range(len(thislist)))

print("---")

# This will print each index number of the list: 0, 1, and 2
for i in range(len(thislist)):
  print(i)

# This will print each item in the list
# by specifing each index number in the list:
for i in range(len(thislist)):
    print(thislist[i])

print("---")

# Loop through with a while loop
thislist = ["apple", "banana", "cherry"]
i = 0
while i < len(thislist):
  print(thislist[i])
  i += 1

 
# Looping with "List Comprehension"
thislist = ["apple", "banana", "cherry"]
[print(x) for x in thislist]


Python - Add or remove items from a list

# Add an item to a list
myList = ["apple", "banana", "cherry"]
myList.append("orange")
print(myList)

# Add multiple items to a list
myList = ["apple", "banana", "cherry"]
tropical = ["mango", "pineapple", "papaya"]
myList.extend(tropical)
# ['apple', 'banana', 'cherry', 'mango', 'pineapple', 'papaya']
print(myList)

# Insert an item mid-list
myList = ["apple", "banana", "cherry"]
myList.insert(1,"orange")
# ['apple', 'orange', 'banana', 'cherry']
print(myList)

# Remove an item from a list
thislist = ["apple", "banana", "cherry"]
thislist.remove("banana")
# ['apple', 'cherry']
print(thislist)

# Pop method to remove the list item from a list
thislist = ["apple", "banana", "cherry"]
thislist.pop()
# ['apple', 'banana']
print(thislist)

# Pop method with an index value targets that item in list
thislist = ["apple", "banana", "cherry"]
thislist.pop(1)
# ['apple', 'cherry']
print(thislist)

# Delete method
thislist = ["apple", "banana", "cherry"]
del thislist[0]
# ['banana', 'cherry']
print(thislist)

# Delete the entire list
thislist = ["apple", "banana", "cherry"]
del thislist
# This will generate an error because the list itself is deleted,
#  not simply emptied of all items.
#print(thislist)

# This is the way to empty all items from a list
thislist = ["apple", "banana", "cherry"]
thislist.clear()
# []
print(thislist)