curriculum/challenges/english/blocks/review-python/67f39e391c9b373069def02c.md
https://www.python.org/).=) and then the value. This can be a number, string, boolean, etc. Here are some examples:name = 'John Doe'
age = 25
Naming Conventions for Variables: Here are the naming conventions you should use for variables:
age, Age, and AGE are all considered unique.if, class, or def.snake_case.# This is a single line comment
"""
This is a multi-line string.
Here is some code commented out.
name = 'John Doe'
age = 25
"""
print() Function: To print data to the console, you can use the print() function like this:print('Hello world!') # Hello world!
my_integer_var = 10
print('Integer:', my_integer_var) # Integer: 10
my_float_var = 4.50
print('Float:', my_float_var) # Float: 4.5
my_string_var = 'hello'
print('String:', my_string_var) # String: hello
True or False:my_boolean_var = True
print('Boolean:', my_boolean_var) # Boolean: True
my_set_var = {7, 5, 8}
print('Set:', my_set_var) # Set: {7, 5, 8}
my_dictionary_var = {"name": "Alice", "age": 25}
print('Dictionary:', my_dictionary_var) # Dictionary: {'name': 'Alice', 'age': 25}
my_tuple_var = (7, 5, 8)
print('Tuple:', my_tuple_var) # Tuple: (7, 5, 8)
my_range_var = range(5)
print(my_range_var) # range(0, 5)
my_list = [22, 'Hello world', 3.14, True]
print(my_list) # [22, 'Hello world', 3.14, True]
my_none_var = None
print('None:', my_none_var) # None: None
None.type() Function: To see the type for a variable, you can use the type() function like this:greeting = 'Hello there!'
age = 21
print(type(greeting)) # <class 'str'>
print(type(age)) # <class 'int'>
isinstance() Function: This is used to check if a variable matches a specific data type:greeting = 'Hello world'
name = 'John Doe'
print(isinstance(greeting, str)) # True
print(isinstance(name, int)) # False
developer = 'Jessica'
city = "Los Angeles"
my_str = 'Hello world'
print(my_str[0]) # H
print(my_str[6]) # w
print(my_str[-1]) # d
print(my_str[-2]) # l
\) if your string contains quotes like this:msg = 'It\'s a sunny day'
quote = "She said, \"Hello!\""
+ operator like this:developer = 'Jessica'
print('My name is ' + developer + '.') # My name is Jessica.
Another way to concatenate strings is by using the += operator. This is used to perform concatenation and assignment in the same step like this:
greeting = 'My name is '
developer = 'Jessica.'
greeting += developer
print(greeting) # My name is Jessica.
f-strings: This is short for formatted string literals. It allows you to handle interpolation and also do some concatenation with a compact and readable syntax:developer = 'Jessica'
greeting = f'My name is {developer}.'
print(greeting) # My name is Jessica.
str[start:stop:step]
The start position represents the index where the extraction should begin. The stop position is where the slice should end. This position is non-inclusive. The step position represents the interval to increment for the slicing. Here are some examples:
message = 'Python is fun!'
print(message[0:6]) # Python
print(message[7:]) # is fun!
print(message[::2]) # Pto sfn
len() function is used to return the number of the characters in the string:developer = 'Jessica'
print(len(developer)) # 7
in operatorin Operator: This returns a boolean that specifies whether the character or characters exist in the string or not:my_str = 'Hello world'
print('Hello' in my_str) # True
print('hey' in my_str) # False
print('hi' in my_str) # False
print('e' in my_str) # True
print('f' in my_str) # False
upper(): This string method returns a new string with all characters converted to uppercase:developer = 'Jessica'
print(developer.upper()) # JESSICA
lower(): This string method returns a new string with all characters converted to lowercase:developer = 'Jessica'
print(developer.lower()) # jessica
strip(): This string method returns a copy of the string with specified leading and trailing characters removed (if no argument is passed to the method, it removes leading and trailing whitespace).greeting = ' hello world '
trimmed_my_str = greeting.strip()
print(trimmed_my_str) # 'hello world'
replace(): This returns a new string with all occurrences of the old string replaced by a new one.greeting = 'hello world'
replaced_my_str = greeting.replace('hello', 'hi')
print(replaced_my_str) # 'hi world'
split(): This is used to split a string into a list using a specified separator. A separator is a string specifying where the split should happen.dashed_name = 'example-dashed-name'
split_words = dashed_name.split('-')
print(split_words) # ['example', 'dashed', 'name']
join(): This is used to join elements of an iterable into a string with a separator. An iterable is a collection of elements that can be looped over like a list, string or a tuple.example_list = ['example', 'dashed', 'name']
joined_str = ' '.join(example_list)
print(joined_str) # example dashed name
startswith(prefix): This string method returns a boolean indicating if a string starts with the specified prefix:developer = 'Naomi'
result = developer.startswith('N')
print(result) # True
endswith(suffix): This string method returns a boolean indicating if a string ends with the specified suffix:developer = 'Naomi'
result = developer.endswith('N')
print(result) # False
find(): This string method returns the index for the first occurrence of a substring. If one is not found, then -1 is returned:developer = 'Naomi'
result = developer.find('N')
print(result) # 0
city = 'Los Angeles'
print(city.find('New')) # -1
count(substring): This string method counts how many times a substring appears in a string:city = 'Los Angeles'
print(city.count('e')) # 2
capitalize(): This string method returns a new string with the first character capitalized and the other characters lowercased:dessert = 'chocolate cake'
print(dessert.capitalize()) # Chocolate cake
isupper(): This string method returns True if all letters in the string are uppercase and False if otherwise:dessert = 'chocolate cake'
print(dessert.isupper()) # False
islower(): This string method returns True if all letters in the string are lowercase and False if otherwise:dessert = 'chocolate cake'
print(dessert.islower()) # True
title(): This string method returns a new string with the first letter of each word capitalized:city = 'los angeles'
print(city.title()) # Los Angeles
maketrans(): This string method is used to create a table of 1 to 1 character mappings for translation. It is often used with the translate() method which applies that table to a string and return the translated result.trans_table = str.maketrans('abc', '123')
print(trans_table) # {97: 49, 98: 50, 99: 51}
result = 'abcabc'.translate(trans_table)
print(result) # 123123
int_1 = 56
int_2 = 12
float_1 = 5.4
float_2 = 12.0
# Addition
print('Integer Addition:', int_1 + int_2) # Integer Addition: 68
print('Float Addition:', float_1 + float_2) # Float Addition: 17.4
# Subtraction
print('Int Subtraction:', int_1 - int_2) # Int Subtraction: 44
print('Float Subtraction:', float_2 - float_1) # Float Subtraction: 6.6
# Multiplication
print('Int Multiplication:', int_1 * int_2) # Int Multiplication: 672
print('Float Multiplication:', float_2 * float_1) # Float Multiplication: 64.80000000000001
# Division
print('Int Division:', int_1 / int_2) # Int Division: 4.666666666666667
print('Float Division:', float_2 / float_1) # Float Division: 2.222222222222222
When you add a float and an integer, the result will be converted to a float like this:
int_1 = 56
float_1 = 5.4
print(int_1 + float_1) # 61.4
%): This returns the remainder when a number is divided by another number:int_1 = 56
int_2 = 12
print(int_1 % int_2) # 8
//): This operator is used to divide two numbers and round down the result to the nearest whole number:int_1 = 56
int_2 = 12
print(int_1 // int_2) # 4
**): This operator is used to raise a number to the power of another:int_1 = 4
int_2 = 2
print(int_1 ** int_2) # 16
float() Function: You can use this function to convert an integer to a float.num = 4
print(float(num)) # 4.0
int() Function: You can use this function to convert a float to an integer.num = 4.0
print(int(num)) # 4
round() Function: This is used to round a number to the nearest whole integer:num_1 = 3.4
num_2 = 7.7
print(round(num_1)) # 3
print(round(num_2)) # 8
abs() Function: This is used to return the absolute value of a number:num = -13
print(abs(num)) # 13
bin() Function: This is used to convert an integer to its binary representation as a string:num = 56
print(bin(num)) # 0b111000
oct() Function: This is used to convert an integer to its octal representation as a string:num = 56
print(oct(num)) # 0o70
hex() Function: This is used to convert an integer to its hexadecimal representation as a string:num = 56
print(hex(num)) # 0x38
pow() Function: This is used to raise a number to the power of another:result = pow(2, 3)
print(result) # 8
# Addition assignment
my_var = 10
my_var += 5
print(my_var) # 15
# Subtraction assignment
count = 14
count -= 3
print(count) # 11
# Multiplication assignment
product = 65
product *= 7
print(product) # 455
# Division assignment
price = 100
price /= 4
print(price) # 25.0
# Floor Division assignment
total_pages = 23
total_pages //= 5
print(total_pages) # 4
# Modulo assignment
bits = 35
bits %= 2
print(bits) # 1
# Exponentiation assignment
power = 2
power **= 3
print(power) # 8
There are other augmented assignment operators too, like those for bitwise operators. They include &=, ^=, >>=, and <<=.
# Defining a function
def get_sum(num_1, num_2):
return num_1 + num_2
result = get_sum(3, 4) # function call
print(result) # 7
If a function does not explicitly return a value, then the default return value is None:
def greet():
print('hello')
result = greet() # hello
print(result) # None
You can also supply default values to parameters like this:
def get_sum(num_1, num_2=2):
return num_1 + num_2
result = get_sum(3)
print(result) # 5
If you call the function without the correct number of arguments, you will get a TypeError:
def calculate_sum(a, b):
print(a + b)
calculate_sum()
# TypeError: calculate_sum() missing 2 required positional arguments: 'a' and 'b'
input() Function: This is used to prompt the user for some input:name = input('What is your name?') # User types 'Kolade' and presses Enter
print('Hello', name) # Hello Kolade
int() Function: This is used to convert a number, boolean, or a numeric string into an integer:print(int(3.14)) # 3
print(int('42')) # 42
print(int(True)) # 1
print(int(False)) # 0
def my_func():
num = 10
print(num)
def outer_func():
msg = 'Hello there!'
def inner_func():
print(msg)
inner_func()
print(outer_func()) # Hello there!
tax = 0.70
def get_total(subtotal):
total = subtotal + (subtotal * tax)
return total
print(get_total(100)) # 170.0
print(str(45)) # '45'
print(type(3.14)) # <class 'float'>
print(isinstance(3, str)) # False
==): Checks if two values are equal:print(3 == 4) # False
!=): Checks if two values are not equal:print(3 != 4) # True
>): Checks if one value is greater than another:print(3 > 4) # False
<): Checks if one value is less than another:print(3 < 4) # True
>=): Checks if one value is greater than or equal to another:print(3 >= 4) # False
<=): Checks if one value is less than or equal to another:print(3 <= 4) # True
if, elif and else Statementsif Statements: These are conditions used to determine if something is true or not. If the condition evaluates to True, then that block of code will run.age = 18
if age >= 18:
print('You are an adult') # You are an adult
elif Statement: These are conditions that come after an if statement. If the elif condition evaluates to True, then that block of code will run.age = 16
if age >= 18:
print('You are an adult')
elif age >= 13:
print('You are a teenager') # You are a teenager
else Clause: This will run if no other conditions evaluate to True.age = 12
if age >= 18:
print('You are an adult')
elif age >= 13:
print('You are a teenager')
else:
print('You are a child') # You are a child
You can also use nested if statements like this:
is_citizen = True
age = 25
if is_citizen:
if age >= 18:
print('You are eligible to vote') # You are eligible to vote
else:
print('You are not eligible to vote')
True or False in a logical context. Many values are considered truthy, that is, they evaluate to True in a logical context. Others are falsy, meaning they evaluate to False. Here are some examples of falsy values:None
False
Integer 0
Float 0.0
Empty strings ''
Other values like non-zero numbers, and non-empty strings are truthy.
bool() Functionbool() function. It explicitly converts a value to its boolean equivalent and returns True for truthy values and False for falsy values. Here are a few examples:print(bool(False)) # False
print(bool(0)) # False
print(bool('')) # False
print(bool(True)) # True
print(bool(1)) # True
print(bool('Hello')) # True
and, or, and not.and Operator: This operator takes two operands and returns the first operand if it is falsy, otherwise, it returns the second operand. Both operands must be truthy for an expression to result in a truthy value.is_citizen = True
age = 25
print(is_citizen and age) # 25
You can also use the and operator in conditionals like this:
is_citizen = True
age = 25
if is_citizen and age >= 18:
print('You are eligible to vote') # You are eligible to vote
else:
print('You are not eligible to vote')
or Operator: This operator returns the first operand if it is truthy, otherwise, it returns the second operand. An or expression results in a truthy value if at least one operand is truthy. Here is an example:age = 19
is_employed = False
print(age or is_employed) # 19
Just like with the and operator, you can use the or operator in conditionals like this:
age = 19
is_student = True
if age < 18 or is_student:
print('You are eligible for a student discount') # You are eligible for a student discount
else:
print('You are not eligible for a student discount')
and and or operators are known as short-circuit operators. Short-circuiting means Python checks values from left to right and stops as soon as it determines the final result.not Operator: This operator takes a single operand and inverts its boolean value. It converts truthy values to False and falsy values to True. Unlike the previous operators we looked at, not always returns True or False. Here are some examples:print(not '') # True, because empty string is falsy
print(not 'Hello') # False, because non-empty string is truthy
print(not 0) # True, because 0 is falsy
print(not 1) # False, because 1 is truthy
print(not False) # True, because False is falsy
print(not True) # False, because True is truthy
Here is an example of the not operator in a conditional:
is_admin = False
if not is_admin:
print('Access denied for non-administrators.') # Access denied for non-administrators.
else:
print('Welcome, Administrator!')
cities = ['Los Angeles', 'London', 'Tokyo']
cities list, you can reference its index number in the sequence:cities = ['Los Angeles', 'London', 'Tokyo']
cities[0] # Los Angeles
-1 as the index number:cities = ['Los Angeles', 'London', 'Tokyo']
cities[-1] # Tokyo
Negative indexing is used to access elements starting from the end of the list instead of the beginning at index 0.
Creating Lists Using the list() constructor: Lists can also be created using the list() constructor. The list() constructor is used to convert an iterable into a list:
developer = 'Jessica'
print(list(developer))
# Result: ['J', 'e', 's', 's', 'i', 'c', 'a']
len() function to get the length of a list:numbers = [1, 2, 3, 4, 5]
len(numbers) # 5
programming_languages = ['Python', 'Java', 'C++', 'Rust']
programming_languages[0] = 'JavaScript'
print(programming_languages) # ['JavaScript', 'Java', 'C++', 'Rust']
IndexError:programming_languages = ['Python', 'Java', 'C++', 'Rust']
programming_languages[10] = 'JavaScript'
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
"""
del keyword:developer = ['Jane Doe', 23, 'Python Developer']
del developer[1]
print(developer) # ['Jane Doe', 'Python Developer']
in keyword can be used to check if an element exists in a list:programming_languages = ['Python', 'Java', 'C++', 'Rust']
'Rust' in programming_languages # True
'JavaScript' in programming_languages # False
developer = ['Alice', 25, ['Python', 'Rust', 'C++']]
2 since lists are zero-based indexed.developer = ['Alice', 25, ['Python', 'Rust', 'C++']]
developer[2] # ['Python', 'Rust', 'C++']
1:developer = ['Alice', 25, ['Python', 'Rust', 'C++']]
developer[2][1] # Rust
developer list into new variables called name, age and job like this:developer = ['Alice', 34, 'Rust Developer']
name, age, job = developer
If the number of variables on the left side of the assignment operator doesn't match the total number of items in the list, then you will receive a ValueError.
Collecting Remaining Items From a List: To collect any remaining elements from a list, you can use the asterisk (*) operator like this:
developer = ['Alice', 34, 'Rust Developer']
name, *rest = developer
:. To slice a list that starts at index 1 and ends before index 3, you can use the following syntax:desserts = ['Cake', 'Cookies', 'Ice Cream', 'Pie']
desserts[1:3] # ['Cookies', 'Ice Cream']
numbers = [1, 2, 3, 4, 5, 6]
numbers[1::2] # [2, 4, 6]
append() method to add the number 6 to this numbers list:numbers = [1, 2, 3, 4, 5]
numbers.append(6)
print(numbers) # [1, 2, 3, 4, 5, 6]
append() method can also be used to add one list at the end of another:numbers = [1, 2, 3, 4, 5]
even_numbers = [6, 8, 10]
numbers.append(even_numbers)
print(numbers) # [1, 2, 3, 4, 5, [6, 8, 10]]
6, 8, and 10 to the end of the numbers list:numbers = [1, 2, 3, 4, 5]
even_numbers = [6, 8, 10]
numbers.extend(even_numbers)
print(numbers) # [1, 2, 3, 4, 5, 6, 8, 10]
insert() method:numbers = [1, 2, 3, 4, 5]
numbers.insert(2, 2.5)
print(numbers) # [1, 2, 2.5, 3, 4, 5]
remove() method will only remove the first occurrence of an item in the list:numbers = [1, 2, 3, 4, 5, 5, 5]
numbers.remove(5)
print(numbers) # [1, 2, 3, 4, 5, 5]
numbers = [1, 2, 3, 4, 5]
numbers.pop(1) # The number 2 is returned
pop method, then the last element is removed.numbers = [1, 2, 3, 4, 5]
numbers.pop() # The number 5 is returned
numbers = [1, 2, 3, 4, 5]
numbers.clear()
print(numbers) # []
sort() method is used to sort the elements in place. Here is an example of sorting a random list of numbers in place:numbers = [19, 2, 35, 1, 67, 41]
numbers.sort()
print(numbers) # [1, 2, 19, 35, 41, 67]
numbers = [19, 2, 35, 1, 67, 41]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 2, 19, 35, 41, 67]
print(numbers) # [19, 2, 35, 1, 67, 41]
numbers = [6, 5, 4, 3, 2, 1]
numbers.reverse()
print(numbers) # [1, 2, 3, 4, 5, 6]
programming_languages = ['Rust', 'Java', 'Python', 'C++']
programming_languages.index('Java') # 1
index() method, then the result will be a ValueError.developer = ('Alice', 34, 'Rust Developer')
TypeError:programming_languages = ('Python', 'Java', 'C++', 'Rust')
programming_languages[0] = 'JavaScript'
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: "tuple" object does not support item assignment
"""
developer = ('Alice', 34, 'Rust Developer')
developer[1] # 34
numbers = (1, 2, 3, 4, 5)
numbers[-2] # 4
IndexError:numbers = (1, 2, 3, 4, 5)
numbers[7]
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: tuple index out of range
"""
tuple() constructor. Within the constructor, you can pass in different iterables like strings, lists and even other tuples.developer = 'Jessica'
print(tuple(developer))
# Result: ('J', 'e', 's', 's', 'i', 'c', 'a')
in keyword like this:programming_languages = ('Python', 'Java', 'C++', 'Rust')
'Rust' in programming_languages # True
'JavaScript' in programming_languages # False
developer = ('Alice', 34, 'Rust Developer')
name, age, job = developer
*) operator like this:developer = ('Alice', 34, 'Rust Developer')
name, *rest = developer
pie and cookies can be sliced into a separate tuple:desserts = ('cake', 'pie', 'cookies', 'ice cream')
desserts[1:3] # ('pie', 'cookies')
TypeError as tuples are immutable:developer = ('Jane Doe', 23, 'Python Developer')
del developer[1]
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: "tuple" object doesn't support item deletion
"""
count(): Used to determine how many times an item appears in a tuple. For example, you can check how many times the language 'Rust' appears in the tuple:programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust')
programming_languages.count('Rust') # 2
count() method is not present at all in the tuple, then the return value will be 0:programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust')
programming_languages.count('JavaScript') # 0
If no arguments are passed to the count() method, then Python will return a TypeError.
index(): Used to find the index where a particular item is present in the tuple. Here is an example of using the index() method to find the index for the language 'Java':
programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust')
programming_languages.index('Java') # 1
If the specified item cannot be found, then Python will return a ValueError.
You can pass an optional start index to the index() method to specify where to start searching for the item in the tuple:
programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust', 'Python')
programming_languages.index('Python', 3) # 5
index() method to specify where to stop searching for the item in the tuple:programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust', 'Python', 'JavaScript', 'Python')
programming_languages.index('Python', 2, 5) # 2
sorted(): Used to sort the elements in any iterable and return a new sorted list. Here is an example of creating a new list of numbers using the sorted() function:numbers = (13, 2, 78, 3, 45, 67, 18, 7)
sorted(numbers) # [2, 3, 7, 13, 18, 45, 67, 78]
reverse and key arguments. Here is an example of using the key argument to sort items in a tuple by length:programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust', 'Python')
sorted(programming_languages, key=len)
# Result
# ['C++', 'Rust', 'Java', 'Rust', 'Python', 'Python']
reverse argument like this:programming_languages = ('Rust', 'Java', 'Python', 'C++', 'Rust', 'Python')
print(sorted(programming_languages, reverse=True))
# Result
# ['Rust', 'Rust', 'Python', 'Python', 'Java', 'C++']
Definition: Loops are used to repeat a block of code for a set number of times.
for loop: Used to iterate over a sequence (like a list, tuple or string) and execute a block of code for each item in that sequence. Here is an example of using a for loop to iterate through a list and print each language to the console:
programming_languages = ['Rust', 'Java', 'Python', 'C++']
for language in programming_languages:
print(language)
"""
Result
Rust
Java
Python
C++
"""
for loop to loop through the string code and print out each character:for char in 'code':
print(char)
"""
Result
c
o
d
e
"""
for loops can be nested. Here is an example of using a nested for loop:categories = ['Fruit', 'Vegetable']
foods = ['Apple', 'Carrot', 'Banana']
for category in categories:
for food in foods:
print(category, food)
"""
Result
Fruit Apple
Fruit Carrot
Fruit Banana
Vegetable Apple
Vegetable Carrot
Vegetable Banana
"""
while loop: Repeats a block of code until the condition is False. Here is an example of using a while loop for a guessing game:secret_number = 3
guess = 0
while guess != secret_number:
guess = int(input('Guess the number (1-5): '))
if guess != secret_number:
print('Wrong! Try again.')
print('You got it!')
"""
Result
Guess the number (1-5): 2
Wrong! Try again.
Guess the number (1-5): 1
Wrong! Try again.
Guess the number (1-5): 3
You got it!
"""
break and continue statements: Used in loops to modify the execution of a loop.
The break statement is used to exit the loop immediately when a certain condition is met. Here is an example of using the break statement for a list of developer_names:
developer_names = ['Jess', 'Naomi', 'Tom']
for developer in developer_names:
if developer == 'Naomi':
break
print(developer)
continue statement is used to skip that current iteration and move onto the next iteration of the loop. Here is an example to use the continue statement instead of a break statement:developer_names = ['Jess', 'Naomi', 'Tom']
for developer in developer_names:
if developer == 'Naomi':
continue
print(developer)
for and while loops can be combined with an else clause, which is executed only when the loop was not terminated by a break:words = ['sky', 'apple', 'rhythm', 'fly', 'orange']
for word in words:
for letter in word:
if letter.lower() in 'aeiou':
print(f"'{word}' contains the vowel '{letter}'")
break
else:
print(f"'{word}' has no vowels")
range() function: Used to generate a sequence of integers.range(start, stop, step)
stop argument is an integer (non-inclusive) that represents the end point for the sequence of numbers being generated. Here is an example of using the range() function:for num in range(3):
print(num)
start argument is not specified, then the default will be 0. By default the sequence of integers will increment by 1. You can use the optional step argument to change the default increment value. Here is an example of generating a sequence of even integers from 2 up to but not including 11 (i.e., includes 10)for num in range(2, 11, 2):
print(num)
If you don't provide any arguments to the range() function, then you will get a TypeError.
The range() function only accepts integers for arguments and not floats. Using floats will also result in a TypeError:
ERROR!
Traceback (most recent call last):
File "<main.py>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
step argument to generate a sequence of integers in decrementing order:for num in range(40, 0, -10):
print(num)
range() function can also be used to create a list of integers by using it with the list constructor. The list constructor is used to convert an iterable into a list. Here is an example of generating a list of even integers between 2 and 10 inclusive:numbers = list(range(2, 11, 2))
print(numbers) # [2, 4, 6, 8, 10]
enumerate() and zip() functions in Pythonenumerate(): used to iterate over a sequence and keep track of the index for each item in that sequence. The enumerate() function takes an iterable as an argument and returns an enumerate object that consist of the index and value of each item in the iterable.languages = ['Spanish', 'English', 'Russian', 'Chinese']
for index, language in enumerate(languages):
print(f'Index {index} and language {language}')
# Result
# Index 0 and language Spanish
# Index 1 and language English
# Index 2 and language Russian
# Index 3 and language Chinese
enumerate() function can also be used outside of a for loop:languages = ['Spanish', 'English', 'Russian', 'Chinese']
print(list(enumerate(languages)))
# [(0, 'Spanish'), (1, 'English'), (2, 'Russian'), (3, 'Chinese')]
The enumerate() function also accepts an optional start argument that specifies the starting value for the count. If this argument is omitted, then the count will begin at 0.
zip() : Used to iterate over multiple iterables in parallel. Here's an example using the zip() function to iterate over developers and ids:
developers = ['Naomi', 'Dario', 'Jessica', 'Tom']
ids = [1, 2, 3, 4]
for name, id in zip(developers, ids):
print(f'Name: {name}')
print(f'ID: {id}')
"""
Result
Name: Naomi
ID: 1
Name: Dario
ID: 2
Name: Jessica
ID: 3
Name: Tom
ID: 4
"""
even_numbers = [num for num in range(21) if num % 2 == 0]
print(even_numbers)
filter(): Used to filter elements from an iterable based on a condition. It returns an iterator that contains only the elements that satisfy the condition. Here is an example of creating a new list of just words longer than four characters:words = ['tree', 'sky', 'mountain', 'river', 'cloud', 'sun']
def is_long_word(word):
return len(word) > 4
long_words = list(filter(is_long_word, words))
print(long_words) # ['mountain', 'river', 'cloud']
map(): Used to apply a function to each item in an iterable and return a new iterable with the results. Here is an example of using the map() function to convert a list of celsius temperatures to fahrenheit:celsius = [0, 10, 20, 30, 40]
def to_fahrenheit(temp):
return (temp * 9/5) + 32
fahrenheit = list(map(to_fahrenheit, celsius))
print(fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]
sum(): Used to get the sum from an iterable like a list or tuple. Here is an example of using the sum() function:numbers = [5, 10, 15, 20]
total = sum(numbers)
print(total) # Result: 50
start argument which sets the initial value for the summation. Here is an updated example using the start argument as a positional argument:numbers = [5, 10, 15, 20]
total = sum(numbers, 10) # positional argument
print(total) # 60
start argument as a keyword argument like this instead:numbers = [5, 10, 15, 20]
total = sum(numbers, start=10) # keyword argument
print(total) # 60
numbers = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers) # [2, 4]
dictionary = {
key1: value1,
key2: value2
}
dict() Constructor: The dict() constructor is an alternative way to build the dictionary. You pass a list of tuples as an argument to the dict() constructor. These tuples contain the key as the first element and the value as the second element.pizza = dict([('name', 'Margherita Pizza'), ('price', 8.9), ('calories_per_slice', 250), ('toppings', ['mozzarella', 'basil'])])
dictionary[key]
get() Method: The get() method retrieves the value associated with a key. It's similar to the bracket notation, but it lets you set a default value, preventing errors if the key doesn't exist.dictionary.get(key, default)
keys() and values() Methods: The keys() and values() methods return a view object with all the keys and values in the dictionary, respectively. A view object is a way to see the content of a dictionary without creating a separate copy of the data.pizza = {
'name': 'Margherita Pizza',
'price': 8.9,
'calories_per_slice': 250
}
pizza.keys()
# dict_keys(['name', 'price', 'calories_per_slice'])
pizza.values()
# dict_values(['Margherita Pizza', 8.9, 250])
items() Method: The items() method returns a view object with all the key-value pairs in the dictionary, including both the keys and the values.pizza.items()
# dict_items([('name', 'Margherita Pizza'), ('price', 8.9), ('calories_per_slice', 250)])
clear() Method: The clear() method removes all the key-value pairs from the dictionary.pizza.clear()
pop() Method: The pop() method removes the key-value pair with the key specified as the first argument and returns its value. If the key doesn't exist, it returns the default value specified as the second argument. If the key doesn't exist and the default value is not specified, a KeyError is raised.pizza.pop('price', 10)
pizza.pop('total_price') # KeyError
popitem() Method: In Python 3.7 and above, the popitem() method removes the last inserted item.pizza.popitem()
update() Method: The update() method updates the key-value pairs with the key-value pairs of another dictionary. If they have keys in common, their values are overwritten. New keys will be added to the dictionary as new key-value pairs.pizza.update({ 'price': 15, 'total_time': 25 })
for loop with values() to get all the values of a dictionary.products = {
'Laptop': 990,
'Smartphone': 600,
'Tablet': 250,
'Headphones': 70,
}
for price in products.values():
print(price)
Output:
990
600
250
70
products dictionary above, you can write products.keys() or products directly.for product in products.keys():
print(product)
# Or
for product in products:
print(product)
Output:
Laptop
Smartphone
Tablet
Headphones
products.items(). You get individual tuples with the keys and their corresponding values.for product in products.items():
print(product)
Output:
('Laptop', 990)
('Smartphone', 600)
('Tablet', 250)
('Headphones', 70)
To store the key and value in separate loop variables, you need to separate them with a comma. The first variable stores the key, and the second stores the value.
for product, price in products.items():
print(product, price)
Output:
Laptop 990
Smartphone 600
Tablet 250
Headphones 70
enumerate() Function: If you need to iterate over a dictionary while keeping track of a counter, you can call the enumerate() function. The function returns an enumerate object, which assigns an integer to each item, like a counter. You can start the counter from any number, but by default, it starts from 0.Assigning the index and item to separate loop variables is the common way to use enumerate(). For example, with products.items(), you can get the entire key-value pair in addition to the index:
for index, product in enumerate(products.items()):
print(index, product)
Output:
0 ('Laptop', 990)
1 ('Smartphone', 600)
2 ('Tablet', 250)
3 ('Headphones', 70)
To customize the initial value of the count, you can pass a second argument to enumerate(). For example, here we are starting the count from 1.
for index, product in enumerate(products.items(), 1):
print(index, product)
Output:
1 ('Laptop', 990)
2 ('Smartphone', 600)
3 ('Tablet', 250)
4 ('Headphones', 70)
Sets: Sets are built-in data structures in Python that do not allow duplicate values. Sets are mutable and unordered, which means that their elements are not stored in any specific order, so you cannot use indices or keys to access them. Also, sets can only contain values of immutable data types, like numbers, strings, and tuples.
Defining a Set: To define a set, you need to write its elements within curly brackets and separate them with commas.
my_set = {1, 2, 3, 4, 5}
set() function. Only writing empty curly braces will automatically create a dictionary.set() # Set
{} # Dictionary
add() Method: You can add an element to a set with the add() method, passing the new element as an argument.my_set.add(6)
remove() and discard() Methods: To remove an element from a set, you can either use the remove() method or the discard() method, passing the element you want to remove as an argument. The remove() method will raise a KeyError if the element is not found while the discard() method will not.my_set.remove(4)
my_set.discard(4)
clear() method: The clear() method removes all the elements from the set.my_set.clear()
issubset() and issuperset() Methods: The issubset() and the issuperset() methods check if a set is a subset or superset of another set, respectively.my_set = {1, 2, 3, 4, 5}
your_set = {2, 3, 4, 5}
print(your_set.issubset(my_set)) # True
print(my_set.issuperset(your_set)) # True
isdisjoint() Method: The isdisjoint() method checks if two sets are disjoint, if they don't have elements in common.my_set = {1, 2, 3}
your_set = {4, 5, 6}
print(my_set.isdisjoint(your_set)) # True
|): The union operator | returns a new set with all the elements from both sets.my_set = {1, 2, 3}
your_set = {4, 5, 6}
my_set | your_set # {1, 2, 3, 4, 5, 6}
&): The intersection operator & returns a new set with only the elements that the sets have in common.my_set = {1, 2, 3, 4, 5}
your_set = {2, 3, 4, 6}
my_set & your_set # {2, 3, 4}
-): The difference operator - returns a new set with the elements of the first set that are not in the other set.my_set = {1, 2, 3, 4, 5}
your_set = {2, 3, 4, 6}
my_set - your_set # {1, 5}
^): The symmetric difference operator ^ returns a new set with the elements that are either in the first or the second set, but not both.my_set = {1, 2, 3, 4, 5}
your_set = {2, 3, 4, 6}
my_set ^ your_set # {1, 5, 6}
in Operator: You can check if an element is in a set or not with the in operator.print(5 in my_set)
math, random, re (short for "regular expressions"), and datetime.Import Statement: To access the elements defined in built-in modules, you use an import statement. Import statements are generally written at the top of the file. Import statements work the same for functions, classes, constants, variables, and any other elements defined in the module.
Basic Import Statement: You can use the import keyword followed by the name of the module:
import module_name
Then, if you need to call a function from that module, you would use dot notation, with the name of the module followed by the name of the function.
module_name.function_name()
For example, you would write the following in your code to import the math module and get the square root of 36:
import math
math.sqrt(36)
as followed by the alias at the end of the import statement. This is often used for long module names or to avoid naming conflicts.import module_name as module_alias
For example, to refer to the math module as m in your code, you can assign an alias like this:
import math as m
Then, you can access the elements of the module using the alias:
m.sqrt(36)
from. In this case, the import statement starts with from, followed by the module name, then the import keyword, and finally the names of the elements you want to import.from module_name import name1, name2
Then, you can use these names without the module prefix in your Python script. For example:
from math import radians, sin, cos
angle_degrees = 40
angle_radians = radians(angle_degrees)
sine_value = sin(angle_radians)
cos_value = cos(angle_radians)
print(sine_value) # 0.6427876096865393
print(cos_value) # 0.766044443118978
This is helpful, but it can result in naming conflicts if you already have functions or variables with the same name. Keep it in mind when choosing which type of import statement you want to use.
If you need to assign aliases to these names, you can do so as well, using the as keyword followed by the alias.
from module_name import name1 as alias1, name2 as alias2
*): The asterisk tells Python that you want to import everything in that module, but you want to import it so that you don't need to use the name of the module as a prefix.from module_name import *
For example, if you use this to import the math module, you'll be able to call any function defined in that module without specifying the name of the module as a prefix.
from math import *
print(sqrt(36)) # 6.0
However, this is generally discouraged because it can lead to namespace collisions and make it harder to know where names come from.
if __name__ == '__main__'__name__ Variable: __name__ is a special built-in variable in Python. When a Python file is executed directly, Python sets the value of this variable to the string "__main__". But if the Python file is imported as a module into another Python script, the value of the __name__ variable is set to the name of that module.This is why you'll often find this conditional in Python scripts. It contains the code that you only want to run only if the Python script is running as the main program.
if __name__ == '__main__':
# Code
print("Hello there" will lead to a syntax error with the message, SyntaxError: '(' was never closed, because the code is missing a closing parenthesis.NameError when you try to access a variable or function you have not defined. For instance, if you have the line print(username) in your code without having a username variable defined first, you will get a name error with the message NameError: name 'username' is not defined.TypeError: can only concatenate str (not "int") to str.IndexError if you access an index that does not exist in a list or other sequences like tuple and string. For example, in a Hello world string, the index of the last character is 10. If you go ahead and access a character this way, greet = "hello world"; print(greet[11]), you'll get an error with the message IndexError: string index out of range..append() on a string like "hello".append("!") will lead to an error with the message AttributeError: 'str' object has no attribute 'append'.print function: Inserting print() statements around various points in your code while debugging helps you see the values of variables and how your code flows.pdb): Python provides a pdb module for debugging. It's a part of the Python's standard library, so it's always available to use. With pdb, you can set a trace with the set_trace() method so you can start stepping through the code and inspect variables in an interactive way.try...except: This is used to execute a block of code that might raise an exception. The try block is where you anticipate an error might occur, while the except block takes a specified exception and runs if that specified error is raised. Here's an example:
try:
print(22 / 0)
except ZeroDivisionError:
print('You can\'t divide by zero!')
# You can't divide by zero!
You can also chain multiple except blocks so you can handle more types of exceptions:
try:
number = int(input('Enter a number: '))
print(22 / number)
except ZeroDivisionError:
print('You cannot divide by zero!')
# You cannot divide by zero! prints when you enter 0
except ValueError:
print('Please enter a valid number!')
# Please enter a valid number! prints when you don't enter an integer
else and finally: These blocks extend try...except. If no exception occurs, the else block runs. The finally block always runs regardless of errors.
try:
result = 100 / 4
except ZeroDivisionError:
print('You cannot divide by zero!') # This will not run
else:
print(f'Result is {result}') # Result is 25.0
finally:
print('Execution complete!') # Execution complete!
Exception Object: This lets you access the exception itself for better debugging and printing the direct error message. To access the exception object, you need to use the as keyword. Here's an example:
try:
value = int('This will raise an error')
except ValueError as e:
print(f'Caught an error: {e}')
# Caught an error: invalid literal for int() with base 10: 'This will raise an error'
The raise Statement: This allows you to manually raise an exception. You can use it to throw an exception when a certain condition is met. Here's an example:
def divide(a, b):
if b == 0:
raise ZeroDivisionError('You cannot divide by zero')
return a / b
The raise statement is also useful when you create your own custom exceptions, as you can use it to throw an exception with a custom message. Here's an example of that:
class InvalidCredentialsError(Exception):
def __init__(self, message="Invalid username or password"):
self.message = message
super().__init__(self.message)
def login(username, password):
stored_username = "admin"
stored_password = "password123"
if username != stored_username or password != stored_password:
raise InvalidCredentialsError()
return f"Welcome, {username}!"
Here's a how you can use the login function with the InvalidCredentialsError exception:
# failed login attempt
try:
message = login("user", "wrongpassword")
except InvalidCredentialsError as e:
print(f"Login failed: {e}")
else:
print(message)
# successful login attempt
try:
message = login("admin", "password123")
except InvalidCredentialsError as e:
# This block is not executed because the login was successful
print(f"Login failed: {e}")
else:
# The else block runs if the 'try' block completes without an exception
print(message)
The raise statement can also be used with the from keyword to chain exceptions, showing the relationship between different errors:
def parse_config(filename):
try:
with open(filename, 'r') as file:
data = file.read()
return int(data)
except FileNotFoundError:
raise ValueError('Configuration file is missing') from None
except ValueError as e:
raise ValueError('Invalid configuration format') from e
config = parse_config('config.txt')
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f'{self.name.upper()} says woof woof!')
dog1 = Dog('Jack', 3)
dog2 = Dog('Thatcher', 5)
dog1.bark() # JACK says woof woof!
dog2.bark() # THATCHER says woof woof!
object_name1.method_name()
object_name2.method_name()
__init__() using self, and unique to each object.class Dog:
species = 'French Bulldog' # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
print(Dog.species) # French Bulldog
jack = Dog('Jack')
print(jack.name) # Jack
print(jack.species) # French Bulldog
class Car:
def __init__(self, color, model):
self.color = color
self.model = model
def describe(self):
return f'This car is a {self.color} {self.model}'
my_car_1 = Car('red', 'Tesla Model S')
print(my_car_1.describe()) # This car is a red Tesla Model S
describe method on two different car objects:class Car:
def __init__(self, color, model):
self.color = color
self.model = model
def describe(self):
return f'This car is a {self.color} {self.model}'
my_car_1 = Car('red', 'Tesla Model S')
my_car_2 = Car('green', 'Lamborghini Revuelto')
print(my_car_1.describe()) # Calling method using the dot notation
print(my_car_2.describe()) # Calling method using the dot notation
__init__, __len__, __str__, __eq__). Python uses them internally for built-in operations.class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
def __len__(self):
return self.pages
def __str__(self):
return f"'{self.title}' has {self.pages} pages"
def __eq__(self, other):
return self.pages == other.pages
book1 = Book('Built Wealth Like a Boss', 420)
print(len(book1)) # 420
print(str(book1)) # 'Built Wealth Like a Boss' has 420 pages
Calling dunder methods indirectly: You don't need to call dunder methods directly. Instead, Python automatically calls them when certain actions happen. These operations include:
arithmetic operations like addition, subtraction, multiplication, division, and others. __add__() is called for addition, __sub__() for subtraction, __mul__() for multiplication, and __truediv__() for division.
string operations like concatenation, repetition, formatting, and conversion to text. __add__() is called for concatenation, __mul__() for repetition, __format__() for formatting, __str__() and __repr__() for text conversion, and so on.
comparison operations like equality, less-than, greater-than, and others. __eq__() is called for equality checks, __lt__() for less-than, __gt__() for greater-than, and so on.
iteration operations like making an object iterable and advancing through items. __iter__() is called to return an iterator and __next__() to fetch the next item.
class Cart:
def __init__(self):
self.items = []
def add(self, item):
self.items.append(item)
def remove(self, item):
if item in self.items:
self.items.remove(item)
else:
print(f'{item} is not in cart')
def list_items(self):
return self.items
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __contains__(self, item):
return item in self.items
def __iter__(self):
return iter(self.items)
cart = Cart()
cart.add('Laptop')
print(len(cart)) # 1
print('Laptop' in cart) # True
class Wallet:
def __init__(self, balance):
self.__balance = balance # Private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount # Add to the balance safely
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount # Remove from the balance safely
account = Wallet(500)
print(account.__balance) # AttributeError: 'Wallet' object has no attribute '__balance'
@property decorator. Here's a getter that gets the radius of a circle:class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self): # A getter to get the radius
return self._radius
@property
def area(self): # A getter to calculate area
return 3.14 * (self._radius ** 2)
my_circle = Circle(3)
print(my_circle.radius) # 3
print(my_circle.area) # 28.26
@<property_name>.setter above it:class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self): # A getter to get the radius
return self._radius
@radius.setter
def radius(self, value): # A setter to set the radius
if value <= 0:
raise ValueError('Radius must be positive')
self._radius = value
my_circle = Circle(3)
print('Initial radius:', my_circle.radius) # Initial radius: 3
my_circle.radius = 8
print('After modifying the radius:', my_circle.radius) # After modifying the radius: 8
my_circle.radius # This will call the getter
my_circle.radius = 4 # This will call the setter
When setting a value, you should not assign to the property name itself because that will cause a RecursionError. Use a separate internal name, often with an underscore, to store the value.
deleter. A deleter runs custom logic when you use the del statement on a property. To create a deleter, you use the @<property_name>.deleter decorator. # Deleter
@radius.deleter
def radius(self):
print("Deleting radius...")
del self._radius
class Parent:
# Parent attributes and methods
class Child(Parent):
# Child inherits, extends, and/or overrides where necessary
class Parent:
# Attributes and methods for Parent
class Child:
# Attributes and methods for Child
class GrandChild(Parent, Child):
# GrandChild inherits from both Parent and Child
# GrandChild can combine or override behavior from each
super() Function: A function that lets you call a method from a parent class, when a class has a different implementation of that method, or it extends the method, without duplicating code.class A:
def action(self): ...
class B:
def action(self): ...
class C:
def action(self): ...
Class().method() # Works for A, B, or C
__attribute into _ClassName__attribute.class Parent:
def __init__(self):
self.__data = 'Parent data'
class Child(Parent):
def __init__(self):
super().__init__()
self.__data = 'Child data'
c = Child()
print(c.__dict__) # {'_Parent__data': 'Parent data', '_Child__data': 'Child data'}
abc module. The module provides the ABC class (abstract base class) and the @abstractmethod decorator. An abstract base class (ABC) defines the common methods and properties subclasses must implement. It can't be instantiated.@abstractmethod and must be overridden in subclasses, even if it has a default implementation. The basic syntax of abstraction looks like this:from abc import ABC, abstractmethod
# Define an abstract base class
class AbstractClass(ABC):
@abstractmethod
def abstract_method(self):
pass
# Concrete subclass that implements the abstract method
class ConcreteClassOne(AbstractClass):
def abstract_method(self):
print('Implementation in ConcreteClassOne')
# Another concrete subclass
class ConcreteClassTwo(AbstractClass):
def abstract_method(self):
print('Implementation in ConcreteClassTwo')
Algorithms: A set of unambiguous instructions for solving a problem or carrying out a task. Algorithms must finish in a finite number of steps and each step must be precise and unambiguous.
Big O Notation: Describes the worst-case performance, or growth rate, of an algorithm as the input size increases. It focuses on how resource usage grows with input size, ignoring constant factors and lower-order terms.
def check_even_or_odd(number):
if number % 2 == 0:
return 'Even'
else:
return 'Odd'
O(log n) - Logarithmic Time: Time increases slowly as input grows. Common in algorithms that repeatedly reduce problem size by a fraction (like Binary Search).
O(n) - Linear Time: Running time increases proportionally to input size.
for grade in grades:
print(grade)
O(n log n) - Log-Linear Time: Common time complexity of efficient sorting algorithms like Merge Sort and Quick Sort.
O(n²) - Quadratic Time: Running time increases quadratically. Often seen in nested loops.
for i in range(n):
for j in range(n):
print("Hello, World!")
Understanding the Problem: Read the problem statement multiple times. Identify the input, expected output, and how to transform input to output.
Pseudocode: High-level description of algorithm logic that is language-independent. Uses common written language mixed with programming constructs like IF, ELSE, FOR, WHILE.
GET original_string
SET reversed_string = ""
FOR EACH character IN original_string:
ADD character TO THE BEGINNING OF reversed_string
DISPLAY reversed_string
Static Arrays: Have a fixed size determined at initialization. Elements stored in adjacent memory locations. Size cannot be changed during program execution.
Dynamic Arrays: Can grow or shrink automatically during program execution. Handle resizing through automatic copying to larger arrays when needed.
numbers = [3, 4, 5, 6]
# Access elements
numbers[0] # 3
# Update elements
numbers[2] = 16
# Add elements
numbers.append(7)
numbers.insert(3, 15) # Insert at specific index
# Remove elements
numbers.pop(2) # Remove at specific index
numbers.pop() # Remove last element
Stacks: Last-In, First-Out (LIFO) data structure. Elements added and removed from the top only.
Push Operation: Adding an element to the top of the stack. Time complexity: O(1).
Pop Operation: Removing an element from the top of the stack. Time complexity: O(1).
# Using Python list as stack
stack = []
# Push operations
stack.append(1)
stack.append(2)
stack.append(3)
# Pop operations
top_element = stack.pop() # Returns 3
Queues: First-In, First-Out (FIFO) data structure. Elements added to the back and removed from the front.
Enqueue Operation: Adding an element to the back of the queue. Time complexity: O(1).
Dequeue Operation: Removing an element from the front of the queue. Time complexity: O(1).
from collections import deque
# Using deque for efficient queue operations
queue = deque()
# Enqueue operations
queue.append(1)
queue.append(2)
queue.append(3)
# Dequeue operations
first_element = queue.popleft() # Returns 1
None.Map (Abstract Data Type): Manages collections of key-value pairs. Every key must be unique, but values can be repeated.
Hash Map: Concrete implementation of map ADT using hashing technique. Uses hash function to generate hash values for keys, which determine storage location in underlying array.
# Creating dictionaries
my_dictionary = {
"A": 1,
"B": 2,
"C": 3
}
# Alternative creation
my_dictionary = dict(A=1, B=2, C=3)
# Access and modify
value = my_dictionary["A"] # 1
my_dictionary["A"] = 4 # Update value
del my_dictionary["A"] # Remove key-value pair
# Check membership
"C" in my_dictionary
# Get keys, values, items
my_dictionary.keys()
my_dictionary.values()
my_dictionary.items()
Sets: Unordered collections of unique elements. No duplicates allowed, no specific order maintained.
Immutable Elements Only: Sets can only contain immutable data types (numbers, strings, tuples) because hash values must remain constant.
# Creating sets
numbers = {1, 2, 3, 4}
empty_set = set() # Must use set(), not {}
# Add and remove elements
numbers.add(5)
numbers.remove(4) # Raises KeyError if not found
numbers.discard(4) # No error if not found
# Set operations
set_a = {1, 2, 3, 4}
set_b = {2, 3, 4, 5, 6}
# Union, intersection, difference, symmetric difference
set_a.union(set_b) # or set_a | set_b
set_a.intersection(set_b) # or set_a & set_b
set_a.difference(set_b) # or set_a - set_b
set_a.symmetric_difference(set_b) # or set_a ^ set_b
# Subset and superset checks
set_a.issubset(set_b)
set_a.issuperset(set_b)
set_a.isdisjoint(set_b)
# Membership testing
5 in numbers
Hash Collision: Occurs when two different keys produce the same hash value.
Collision Resolution Strategies:
Searching algorithms let you search for a target within a certain list of items.
In computer science, there are two searching algorithms you should know about. They are linear search and binary search algorithms. It is important to understand the differences between the two algorithms and when to use each one.
-1, which means invalid index in most programming languages.O(n) because the time it takes to search through the list grows linearly with the size of the list.O(1) because it doesn't require any additional space to search through the list.-1O(log n) because the time it takes to search through the list grows logarithmically with the size of the list.O(1) because it doesn't require any additional space to search through the list.O(n) because the time it takes to search through the list grows linearly with the size of the list.O(log n) because the time it takes to search through the list grows logarithmically with the size of the list.In computer science, divide-and-conquer is a technique used to break down a problem into smaller sub-problems so they are easier to solve. Recursion is the technique often employed in divide-and-conquer, and divide-and-conquer is a powerful strategy used to implement many efficient sorting algorithms like merge sort.
O(n log n) because the list is continuously divided in half (log n) and then merged together (O(n)).O(n) because it is not an in-place sorting algorithm.A graph is a set of nodes (vertices) connected by edges (connections). Each node can connect to multiple other nodes, forming a network. The different types of graphs include:
Graphs are used in various applications such as maps, networks, recommendation systems, dependency resolution.
This involves visiting all the nodes in a graph. The two main algorithms are:
Breadth-First Search (BFS)
Depth-First Search (DFS)
Graphs can be represented in two main ways:
Adjacency List
Adjacency Matrix
A tree is a special type of graph that is acyclic and connected. Key properties include:
The most common types of trees are:
Binary Trees
Binary Search Trees (BST)
Also known as prefix trees, they are used to store sets of strings, where each node represents a character.
Shared prefixes are stored only once, making them efficient for tasks like autocomplete and spell checking.
Search and insertion operations have a time complexity of O(L), where L is the length of the string.
A priority queue is an abstract data type where each element has a priority.
Queues and stacks consider only the order of insertion, while priority queues consider the priority of elements.
Standard queues follow FIFO (First In First Out) and stacks follow LIFO (Last In First Out). However, in a priority queue, elements with higher priority are served before those with lower priority, regardless of their insertion order.
It's a specialized tree-based data structure with a very specific property called the heap property.
The heap property determines the relationship between parent and child nodes. There are two types of heaps:
Max-Heap
Min-Heap
heapq module exampleimport heapq
# Create empty heap
my_heap = []
# Insert elements
heapq.heappush(my_heap, 9)
heapq.heappush(my_heap, 3)
heapq.heappush(my_heap, 5)
print(my_heap) # [3, 9, 5]
# Remove smallest element
print(heapq.heappop(my_heap)) # 3
print(my_heap) # [5, 9]
# Push + Pop in one step
print(heapq.heappushpop(my_heap, 7)) # 5
print(my_heap) # [7, 9]
# Transform list into heap
nums = [5, 7, 3, 1]
heapq.heapify(nums)
my_heap = []
heapq.heappush(my_heap, (3, "A"))
heapq.heappush(my_heap, (2, "B"))
heapq.heappush(my_heap, (1, "C"))
# Removes lowest number = highest priority
print(heapq.heappop(my_heap)) # (1, "C")
def climb_stairs_memo(n, memo={}):
"""Dynamic programming with memoization"""
# Check if we've already calculated this value
if n in memo:
return memo[n] # Return cached result - O(1) lookup!
# Base cases
if n <= 2:
return n
# Calculate once and store in memo for future use
memo[n] = climb_stairs_memo(n-1, memo) + climb_stairs_memo(n-2, memo)
return memo[n]
def climb_stairs_tabulation(n):
"""Dynamic programming with tabulation"""
if n <= 2:
return n
# Create array to store results for all steps from 0 to n
dp = [0] * (n + 1)
dp[1] = 1 # 1 way to reach step 1
dp[2] = 2 # 2 ways to reach step 2
# Build up the solution iteratively
for i in range(3, n + 1):
# Ways to reach step i = ways to reach (i-1) + ways to reach (i-2)
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
You should consider using dynamic programming in the following scenarios:
Review the Python topics and concepts.