0% found this document useful (0 votes)
3 views13 pages

PythonNotes

Python Notes

Uploaded by

John Irving
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
Download as txt, pdf, or txt
0% found this document useful (0 votes)
3 views13 pages

PythonNotes

Python Notes

Uploaded by

John Irving
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1/ 13

Multiple variables can be declared on the same line

ex) sword_name, sword_damage, sword_length = "Excalibur", 10, 200

Functions can be used to reuse and organize code.


use 'def' to define a new function.
ex) def area_of_a_circle(r):
pi = 3.14
return pi * r * r
After a function has been defined, to "call" a function, pass in any number
as input
ex) radius = 5
area = area_of_a_circle(radius)
print(area)
#78.6

radius = 6
area = area_of_a_circle(radius)
print(area)
#113.04

Functions can have multiple parameters/inputs, seperated by commas.


ex) def subtract(a, b):
result = a - b
return result

ex) def create_introduction(name, age, height, weight)


first_part = "Your name is " + name + ". You are " + age + "years
old."
second_part = "You are " + height + " meters tall and weigh " +
weight + "kilograms."
full_intro = first_part + " " + second_part
return full_intro

The above would be called like:


intro = create_introduction("John", 30, 1.8, 80)
print(intro)
# Your name is John. You are 30 years old. You are 1.8 meters
tall and weigh 80 kilograms.

Functions must be declared before use.


Solongas a function is defined before it is called, the order in which
functions are defined does not matter.

Parameters are used when defining a function. Below, a and b are parameters.
ex) def add(a, b)
return a + be
Arguments are the names of the inputs supplied when a function is called.
ex) sum = add(5, 6)

print() is a function that prints a value to the console. It does not return
a valie
return is a keyword that ends the function's execution. IT gives the spcified
value back to the caller. It does NOT print anything to the console.
print() in functions is useful to help debug values, but it MUST be removed
after testing.

Scope refers to where a variable or function name is used. A variable created


within a function is not available outside of that function.
ex) def subtract(x, y)
return x - y
result = subtract(5, 3)
print(x)
# Error! "name 'x' is not defined"

Logic
print(True and True)
# prints True

print(True or False)
# prints True

Bitwise is used for logic in conjuction with binary.


& = and; | = or; - = not
NOT reverses the output, so that TRUE outputs FALSE

Comparision Operators return a boolean value: whether the operation is true or


false.
ex) is_bigger = 5 > 4
is_bigger = True
because 5 is greater than 4, is_bigger is assigned a value of
True

If statements execute code only if its conditions are meters


ex if bob_score > bill_score:
print("Bob Wins!")
print("Game Complete")
if Bob wins, the console will print "Bob Wins!" "Game Complete", if Bob's
score is greater than Bill's. Otherwise only "Game Complete" will be printed.

If-else statements
if statements can be followed by 0 or more elif (standing for else-if)
statements, which can be followed by 0 or more else statements
ex) if score > high_score:
print('High score beat!')
elif score > second_highest_score:
print('you got second place!')
elif score > third_highest_score:
print('you got third place!')
else:
print('Better luck next time')
if the above used all if statements, they could all execute multiple
times

For loops are written in Python as:


for i in range(0, 10):
print(i)

In English this reads as "starting with 0, as long as i is not less


than 10, increment i by 1. Resulting with the console printing 0-9

The body of the For Loop MUST include an indent (conventionally four
spaces)
the range(start, limit, step) The function will not meet or exceed the
limit parameter. The step determeines how much the function incremets or decrements
(+1 is the default)
ex) for i in range(0, 10, 2):
print(i)
#Prints 0,2,4,6,8
ex) for i in range(3, 0, -1)
print(i)
#prints 3,2,1

Lists are called "Arrays" in other languages. Declared using square brackets,
seperated by commas:
ex) inventory = ["Iron Breastplate", "Healing Potion", "Leather Scraps"]
Lists can contain any data type.

Positions (indices) of lists are numbered starting from zero:


ex) names = ["Bob", "Lane", "Alice", "Breanna"]
Index 0: Bob, Index 1: Lane, et c.

Length of a list can be calculated using the len() function:


ex) fruits = ["apple", "banana", "pear"]
length = len(fruits)
#Prints: 3
Lists can be updated as:
inventory = ["Leather", "Healing Potion", "Iron Ore"]
inventort[0] = "Leather Armor"
# replaces index 0 (Leather) with Leather Armor

You can append things to a list by using the .append() method


cards = []
cards.append("nvidia")
cards.append("amd")
# yields cards = ["nvidia", "amd"]

.pop() is the inverse of .append(): it takes off the last item on the list
and returns it for use:
ex) vegetables = ["broccoli", "cabbage", "kale", "tomato"]
last_vegetable = vegetables.pop()
#vegtables = ["broccoli", "cabbage", "kale"]
#last_vegetable = "tomato"

for i in range(0, len(items)):


if items[i] == "Bread":
bread_count += 1
elif items[i] == "Potion":
potion_count += 1
elif items[i] == "Shortsword":
shortsword_count += 1

using items[i] will scan each item in the list, and increment the count for
each item, with [i] being the index of item, icremented by the for loop

If you don't need to no the index numbers of a list, you don't need to pull
them:
trees = ["oak", "pine", "maple"]
for tree in trees:
print(tree)
# This returns oak / pine / maple, without their indices.
Lists can be divided with the slicing operator (:)
Syntax: my_list[ star : stop : step]

ex) scores = [50, 70, 30, 20, 90, 10, 50]


print(scores[1:5:2]
#prints [70, 20]

Sections can be omitted:


ex) numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[:3] # gives [0, 1, 2]
numbers[3:] # gives [3, 4, 5, 6, 7, 8, 9]

Only the "step" section can be used:


numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[::2] # gives [0, 2, 4, 6, 8]

A negative index counts from the end of the list, so that numbers[-1:]
gives the last item, numbers[-2:] gives the penultimate item et c.
ex) numbers[-3:] #gives [7, 8, 9]

Lists can be merged (concatenated) using the + operator:


total = [1, 2, 3] + [4, 5, 6]
# yields [1, 2, 3, 4, 5, 6]

Lists can be checked if they contained an item (returns True or False)


ex) fruits = ["apple", "orange", "banana"]
print("banana" in fruits)
# prints True

The del keyword can be used to delete individual items or slices.


ex: del nums[3]
deletes the 4th item

del nums[1:3]
# yields [1, 4, 5, 6, 7, 8, 9]

del nums[:]
# removes all items but leaves the empty list.
To find differences between two lists, use a for-in loop, with an if x
not-in loop:
ex) for item in grocery_list:
if item not in items_purchased:
unpurchased_items.append(item)

to find the number of occurences of an item in a list:


l = ["a", "b", "c"}
l.count("b")
yields 2

A tuple is a list with a fixed size, marked by round brackets ()


Tuples are used to store items of different types, which while possible
with lists, it is considered bad practice since it is harder to keep track of what
indices track which types.

Tuples are most often used for groups of 2 or 3 items, such as names
and ages.
When storing single items as tuples, you must include a comma so
Python knows it marks a tuple and not regular parentheses
ex) dog = ("Fido",)

Multiple tuples can be stored within a list. When pulling an item from
a tuple within a list, the first index [0] is the tuple itself, [1] is the first
item et c.
ex) my_tuples = [("this is the first tuple in the list", 45,
True),("this is the second tuple in the list", 21, False)]
print(my_tuples[0][0]) # this is the first tuple in the
list
print(my_tuples[0][1]) # 45
print(my_tuples[1][0]) # this is the second tuple in the
list
print(my_tuples[1][2]) # False

Dictionaries store values in key:value pairs. USed for storing groups of


information.
ex) car = {
"brand": "Tesla",
"model": "3",
"year": 2019
}
Values are retrieved from a dictionary using square brackets like a list:
ex) car = {
"make": tesla,
"model": '3',
}
print(car["make"])
#prints tesla

New values can be assigned by: dict[key] = value


ex) names = ["jack bronson", "jill mcarty", "john denver"]

names_dict = {}
for name in names:
# .split() returns a list of strings
# .split() returns a list of strings
#where each string is a single word from the original
name_lst = name.split()

# here we update the dictionary


names_dict[name_lst[0]] = name_lst[1]

print(names_dict)
# Prints: {'jack': 'bronson', 'jill': 'mcarty', 'john': 'denver'}

Keys can be removed using the del keyword. If a key does not exist, you will
receive an error:
names_dict = {
"jack": "bronson",
"jill": "mcarty",
"joe": "denver"
}
del names_dict["unknown"]
#ERROR

counts[file_type] = counts.get(file_type, 0) + 1
The above line checks if file_type is in the dictionary, if not it usese 0 as
the default count. It then adds 1 to the default or current count.
then it assigns the new value to the dictionary

Before python 3.7, dictionaries were unordered, and you could not iterate
them.

to fetch a value from a nested dictionary, the path as dict["one"]["two"]


["three"]["value"]

to append a dictionary to another dictionary, use the |= operator:


ex) result = {}
result[keys[0]] = values[0]
recursive_result = zipmap(keys[1:], values[1:])
zipped |= result
zipped |= recursive_result

Ensure you understand how to correctly iterate over key-value pairs in a


dictionary with:
for key, value in kwargs.items():

Sets are like Lists, but are unordered and unique- only one value may occur in a
set.
ex) fruits = {"apple", "banana", "grape"}
print(type(fruits)
#prints <class "set">

print(fruits)
#prints {"banana", "grape", "apple"}

to create an empty set, you must use the set() function- using curly brackets
creates a dictionary.
ex) fruits = set()
iterating over values in a set does not guarantee order.

to add an item to a set, use .add():


ex) fruits = {"apple", "banana", "grape"}
fruits.add("pear")
print(fruits)
# prints {"banana", "grape", "pear", "apple"}

to remove an item from a set use .remove:


ex) fruits = {"apple," "banana", "grape"}
fruits.remove("apple")
print(fruits)
#Prints {"banana", "grape"}

Errors and Exceptions:


Exceptions are errors raised during execution. You can use a try-exception to
handle exceptions gracefully.
ex) try:
10 / 0
except Exception as e:
print(e)
#prints "division by zero"
the try block executes until an exception is raised or it completes, which
ever occurs first. The except block is only executes if an exception is raised in
the try block. IT then exposes the exception as data so that the exception can be
handled without crashing.
Classes are essentially custome types. An object is an instance of a class type.
ex) In health = 50 health is an instance of the integer type.

to create a class, use the class keyword and set custom properties as
follows:
ex) class Soldier:
health = 5

to create an instance of the new class, call the class:


first_soldier = Soldier()
print(first_soldier.health)
# prints "5"

Classes can be used to create custom methods, unlike dictionaries which may
seem similar.
A method being a function that is associated with a classm and has access to
all properties of the object.
ex) class soldier:
health = 5

def take_damage(self, damage):


self.health -= damage

soldier_one = Soldier()
soldier_one.take_damage(2)
print(soldier_one.health)
#prints '3'

Methods will always take a special paramater as their first argument called
self. This references to the object itself, allowing you to read and update the
properties of the object.
ex) object.method()

Methods can be used to return values:


ex) def get_speed(self)
speed = 10
speed -= self.armor
speed -= self.num_weapons
return speed

Methods are different from functions in that:


- a method is implicitly passed the object on which it is called, i.e.
you won't see all the inputs in the parameter.
- a method can operate on data that is contained within the class, i.e.
yo won't see all the outputs in the return statement.

Classes generally have thie properties defined using a constructor/initializer


using the init() method.
ex: class Soldier:
def __init__(self):
self.armor = 2
self.num_weapons = 2
These properties can be altered with use of parameters (since the constructor
is a method)
ex) class Solder:
def __init__(self, armor, num_weapons):
self.armor = armor
self.num_weapons = num_weapons
soldier = Soldier(5, 10)
print(soldier.armor)
#print "5"
print(soldier.num_weapons)
#prints "10"
Instance variables vary from object to objectt, and are declared in the
constructor:
ex) class Wall:
def __init__(self):
self.height = 10

south_wall = Wall()
south_wall.height = 20 #only updates this instance of a
wall
print(south_wall.height)
#prints "20"

north_wall = Wall()
print(north_wall.height)
#prints "10"

Class variables remain the same between instances of the same class are
declared at the top levels. It is generally best not to use class variables.
class Wall:
height = 10

south_wall = Wall()
print(south_wall.height)
# prints "10"

Wall.height = 20 # updates all instances of a Wall

print(south_wall.height)
# prints "20"

Encapsulation:
Makes code easier for humans to understand by hiding superfluous information.
A function is an example of encapsulation- the caller doesn't need to interact with
what's inside it, just what it takes in and puts out.

Code can also be encapsulated using private and public members. A user can
interact with a public member, but not private members.

Private members are prefixed with a double underscore (__)


ex) class Wall:
def __init__(self, height)
self.__height = height
def get_height(self):
return self.height
the __height property is private, and cannot be edited by a
user, but it can be seen do you the get_height function.

Attempting to alter a private member will result in an


error:
ex)front_wall.__height = 10 # results in an error.

While definitions are always changing, I like to think about abstraction and
encapsulation in the following way.

Abstraction is a technique that helps us identify what information and behavior


should be encapsulated, and what should be exposed.
Encapsulation is the technique for organizing the code to encapsulate what
should be hidden, and make visible what is intended to be visible.

For example, using the double underscore convention to hide a __speed


variable in Python is an encapsulation technique. In C++ we would use the private
keyword to accomplish the same thing. Alternatively, creating a public method
called move() that uses the encapsulated __speed variable would be an abstraction.

OOP groups data and behavior together, treating programming as a modeling problem:
"How can I create a class that simulates data and behavior of a real human?"
Functional programing treats code as inputs and outputs: "When a human acts, what
are the inputs and what are the outputs that affect my program state?"

Inheritance allows one class to inherit the properties and methods of another class
(the parent class).
One class can inherit from another using the following syntax:
class Animal:
#parent "Animal" class
class Cow(Animal):
# child class "Cow" inherits "Animal"

To use the constructor of another class, use the built-in super() method:
class Animal:
def __init__(self, num_legs):
self.num_legs = num_legs

class Cow(Animal):
def __init_(self)
#call the parent constructor to give cow legs
super().__init__(4)
When a child class inherits from its parent, it inherits everything

There is no limit to how deeply we can nest an inheritance tree.


For example, a Cat can inherit from an Animal that inherits from LivingThing.

That said, we should always be careful that each time we inherit from a base
class the child is a strict subset of the parent.
You should never think to yourself "my child's class needs a couple of the
parent's methods, but not these other ones" and still decide to inherit from that
parent.

Polymorphism describes the trait of OOP that allows classes in the same
hierarchical tree to have methods with the same name, but different behaviors.
ex) class Creature():
def move(self):
print("the creature moves")

class Dragon(Creature):
def move(self):
print("the dragon flies")

class Kraken(Creature):
def move(self):
print("the kraken swims")
for creature in [Creature(), Dragon(), Kraken()]
creature.move()

# prints:
# the creature moves
# the dragon flies
# the kraken swims

i.e. the ability to present the same interface (function or method


signatures) for many different underlying forms:
by making each class responsible for both its data and its code, you
achieve polymorphism.
for example: classes Rectangle, Circle, and Triangle inherit from the
Shape class. Each class has its own draw_shape() method.
This allows us to treat the shapes the same even though they are
different- it hides complex differences behind abstraction.

A function signature (or method signature) includes the name, inputs,


and outputs of a function or method.
ex. class Human:
def hit_by_fire(self):
self.health -= 5
return self.health

class Archer:
def hit_by_fire(self):
self.health -= 10
return self.health

Both methods have the same name, take 0 inputs, and return integers. If
any of those things were different, they would have different function signatures.

When overriding methods, use the same function signature

Custom classes don't have support for operators such as + by default. They
can be added manually however, such as below:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, point):


x = self.x + point.x
y = self.y + point.year
return Point(x, y)
p1 = Point(4, 5)
p2 = Point(2, 3)
p3 = p1 + p2
# p3 is (6, 8)
# without the __add__ function, p3 = p1 + p2 would raise an error.

operator overloading is the practice of defining custom behavior for standard


Python operators. Here's a list of how the operators translate into method names.

Use return to send the output of a function to the main() function:


def main():
book_path = "books/frankenstein.txt"
text = get_book_text(book_path)
num_words = get_word_count(text)
print(f"{num_words} found in document")

def get_book_text(path):
with open(path) as f:
return f.read()
def get_word_count(text):
words = text.split()
return len(words)

main()

In Python, you check if a string is empty using the boolean context of the string
in the if condition. However, here, you are using if doc == False: which is not the
Pythonic way to check if a string is empty.

Instead, if doc: is used to check if the string is not empty (equivalent to if


doc != "") and if not doc: is used to check if the string is empty (equivalent to
if doc == "").

Use set() to convert a list et c. into a set.

Use .intersection to find the like entries in two or more sets:


ex)
def get_common_formats(formats1, formats2):
return set(formats1).intersection(ser(formats2))

the function above converts the lists formats1 and formats2 into sets, and
returns the common entries in both.

In Python, the built-in map function takes a function and an iterable (in this case
a list), and returns a new iterable where the function has been applied to each
element of the original iterable.
This allows us to compose functions that operate on entire lists without having to
write a loop and store state in variables
def square(x):
return x * x

nums = [1, 2, 3, 4, 5]
squared_nums = map(square, nums)
print(list(squared_nums))
# [1, 4, 9, 16, 25]

Self:
In Method Definitions:
you must include self as the first parameter in instance of methods of
a class definition. This allows Python to know which instance of the class to use.
class Bear:
def __init__(self, name):
self.name = name

def speak(self):
return f"I am {self.name}, a wizard bear!"

In Method Calls (within the class)


yo do not include self when calling a method or accessing attributes on
the same object. Python already knows to use the instance on the called method.
class Bear:
def __init__(self, name):
self.name = name

def introduce(self):
greeting = self.speak()
return greeting

def speak(self):
return f"I am {self.name}, a wizard bear!"

When calling a Method with super():


You do not include self when calling a superclass method with super().
Just pass the necessary arguments for the method.
class MagicBear(Bear):
def __init__(self, name, magical_power):
super().__init__(name)
self.magical_power = magical_power

Regex (regular Expressions):


In python, it requires the re module. It has a findall function that will
return a list of all the matches in a string.

import re

text = "I'm a little teapot, short and stout.


Here is my handle, here is my spout."
matches = re.findall(r"teapot", text)
print(matches) # ['teapot']

r"teapot" is a regex pattern.


the r tells Python to treat the string as a 'raw' string,
which means we don't have to excape backslashes
The patter teapot will match any exact occurrences of the
word "teapot" in the input

Regex for a phonenumber:

text = "my phone number is 555-555-5555 and my


friend's phone number is 555-555-5556"
matches = re.findall(r"\d{3}-\d{3}-\d{4}",
text)
print(matches) # ['555-555-5555', '555-555-
5556']

\d matches any digit


{3} means "exactly three of the preceding character"
- is a literal - that we want to match

Regex for an email address:

text = "My email is lane@example.com and my


friend's email is hunter@example.com"
matches = re.findall(r"\w+@\w\.\w+', text)
print(matches) # ['lane@example.com',
'hunter@example.com']
\w matches any word character (alphanmeric
characters and underscores)
+ means "one or more of the preceding
character"
@ is a literal match for @
\. is a literal . ( . is a special character in
regex so we escape it with a leading backslash)

Regex for a text between parentheses:

text = "I have a (cat) and a (dog)"


matches - re.findall(r"\((.*\)", text)
print(matches) # ['cat', 'dog']

\( and \) are escaped parentheses that we want


to match
.*? matches any number of characters between
the parentheses (except line terminators)

You might also like