0% found this document useful (0 votes)
2 views17 pages

Python Basics Complete Notes (1)

The document provides comprehensive notes on Python basics, covering topics such as variables, input/output, type conversion, arithmetic and comparison operators, conditionals, loops, lists, dictionaries, and functions. It includes examples and explanations for each concept, illustrating how to use Python effectively for programming. The notes also address common mistakes and provide methods for solving problems related to loops and nested structures.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views17 pages

Python Basics Complete Notes (1)

The document provides comprehensive notes on Python basics, covering topics such as variables, input/output, type conversion, arithmetic and comparison operators, conditionals, loops, lists, dictionaries, and functions. It includes examples and explanations for each concept, illustrating how to use Python effectively for programming. The notes also address common mistakes and provide methods for solving problems related to loops and nested structures.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Python Basics — Complete Notes

Attaullah's Python Foundation: Variables through Nested Loops

1. Variables, Input, and Print


Variables store values. input() always returns a string, so numbers must be converted using int() or float().
name = input("Enter your name ")
age = int(input("Enter your age "))
print(f"My name is {name} I am {age} years old")

Note: str() around input() is redundant since input() already returns a string.

f-strings
The letter f before a string tells Python to evaluate whatever is inside curly braces {} instead of treating it as
plain text.
name = "Ali"
print(f"My name is {name}") # My name is Ali
print("My name is {name}") # My name is {name} (no f, prints literally)

2. Type Conversion
The + operator behaves differently based on type: for strings it concatenates (joins text), for numbers it adds.
num_1 = input("Enter first number ")
num_2 = input("Enter second number ")
print(num_1 + num_2) # "5" + "3" = "53" (string joining, NOT math)

num_1 = int(input("Enter first number "))


num_2 = int(input("Enter second number "))
print(num_1 + num_2) # 5 + 3 = 8 (real addition)

3. Arithmetic Operators
Operator Meaning Example (10, 3)

+ Addition 13

- Subtraction 7

* Multiplication 30

/ Division (float) 3.333...

// Floor division (whole number) 3

% Modulus (remainder) 1

Note: % (modulus) is used constantly for even/odd checks: if num % 2 == 0: means even.
4. Comparison and Logical Operators
>= <= > < == != # comparison operators

and # True only if BOTH sides are True


or # True if EITHER side is True
not # flips True/False

age = 20
if age >= 18 and age <= 60:
print("Working age")
if age < 18 or age > 60:
print("Outside working age")

5. float, bool, type()


price = float(input("Enter price: ")) # decimal numbers
is_student = True # bool: True / False
print(type(age)) # <class 'int'>

6. Basic String Methods


name = "Attaullah"
print(len(name)) # 8 (number of characters)
print([Link]()) # ATTAULLAH
print([Link]()) # attaullah

7. Conditionals: if / elif / else


marks = int(input("Enter your marks: "))

if marks >= 90:


print("Grade A")
elif marks >= 80:
print("Grade B")
elif marks >= 70:
print("Grade C")
elif marks >= 60:
print("Grade D")
else:
print("Fail")

Note: With elif, you don't need upper-bound checks (like marks<90) because Python only reaches an elif if all above
it were False.
8. Loops — while and for
while loop
Repeats code AS LONG AS a condition stays True.
count = 1
while count <= 5:
print(count)
count += 1
# Output: 1 2 3 4 5

Note: If you forget to update the loop variable (count += 1), the condition never becomes False -> INFINITE LOOP.

for loop with range()


range(start, stop, step) generates numbers starting at 'start', stopping BEFORE 'stop'.
range(5) # 0,1,2,3,4 (starts at 0, stops before 5)
range(2, 6) # 2,3,4,5 (starts at 2, stops before 6)
range(0, 10, 2) # 0,2,4,6,8 (step of 2)

for i in range(1, 11):


print(i) # prints 1 through 10

break and continue


for i in range(10):
if i == 5:
break # exits the loop completely
print(i)
# Output: 0 1 2 3 4

for i in range(5):
if i == 2:
continue # skips just this one iteration
print(i)
# Output: 0 1 3 4

else with loops


Runs only if the loop finishes WITHOUT hitting break.
for i in range(5):
print(i)
else:
print("Loop finished without break")

9. Common Loop Mistakes (from practice)


Mistake 1: Wrong starting value changes the whole sequence.
# Wanted even numbers 2-20, but started at 1 -> got odd numbers instead
i = 1
while i <= 20:
print(i)
i += 2 # BUG: gives 1,3,5,7... not 2,4,6,8...
# FIX: start at i = 2

Mistake 2: Increment placed inside an if-block causes infinite loops.


i = 1
while i <= 20:
if i % 2 == 0:
print(i)
i += 1 # BUG: i only increases when even -> stuck forever at i=1
# FIX: move i += 1 OUTSIDE the if, so it runs every iteration
10. Nested Loops
A loop inside another loop. The RULE: the inner loop runs COMPLETELY through all its values before the
outer loop moves to its next value.
for i in range(1, 4): # outer: i = 1, 2, 3
for j in range(1, 3): # inner: j = 1, 2 (every time)
print(i, j)

# Output:
# 1 1
# 1 2
# 2 1
# 2 2
# 3 1
# 3 2

Note: i stays FROZEN for the entire inner loop. It only changes after ALL j-values are used up.

Classic bug: reusing the same variable name


# WRONG - inner loop overwrites outer loop's variable
for i in range(1,6):
for i in range(1,5): # BUG: should be 'j', not 'i'
print(i)

# CORRECT
for i in range(1,6):
for j in range(1,5):
print(j)

The 4-Step Method for finding inner loop range


This is the reliable method built during practice for figuring out any nested loop pattern:
STEP 1 - COUNT: How many items appear in each row of the pattern?
Build a table: i vs count

STEP 2 - FORMULA: Compare i to count in the table.


- Are they equal? -> count = i
- Goes up/down by 1? -> count = i + k or count = k - i
- Doubles/triples? -> count = k * i
- Stays constant? -> count = fixed number
Solve for k using ONE row, then verify with the rest.

STEP 3 - CONVERT TO RANGE (fixed rule, NEVER changes):


range(1, 1 + count)
(because range(1, N) always gives N-1 values, so add 1 back)

STEP 4 - VERIFY: Plug in each i value and confirm it matches the pattern.

Worked Example: Triangle Pattern


Pattern:
1
1 2
1 2 3
1 2 3 4

Step 1: counts are 1,2,3,4 for i=1,2,3,4. Step 2: count = i (identical, no extra number needed). Step 3: range(1,
i+1). Step 4: verified correct for every row.
for i in range(1, 5):
for j in range(1, i + 1):
print(j, end=" ")
print()

Worked Example: Decreasing Pattern


Pattern:
X X X X X
X X X X
X X X
X X
X

Step 1: counts are 5,4,3,2,1. Step 2: count goes down by 1 as i goes up -> count = k - i. Test i=1: 5 = k-1 ->
k=6, so count = 6-i. Step 3: range(1, 7-i). Step 4: verified.
for i in range(1, 6):
for j in range(1, 7 - i):
print('*', end=" ")
print()

Worked Example: Multiplying Pattern


Pattern:
X X
X X X X
X X X X X X

Step 1: counts are 2,4,6. Step 2: count doubles i -> count = 2*i. Step 3: range(1, 1+2*i). Step 4: verified (2,4,6
match).
for i in range(1, 4):
for j in range(1, 1 + 2*i):
print('*', end=" ")
print()

Deciding WHAT to print (i, j, or a formula)


This is a separate skill from finding the range. Four cases seen in practice:
1. Counting/changing numbers in the row -> print(j)
Example: 1 2 3 -> for j in range(1,4): print(j)

2. Same number as the row number, repeated -> print(i)


Example: 2 2 (row i=2) -> for j in range(1,3): print(i)
3. Fixed literal value, unrelated to row -> print the literal
Example: 3 3 3 (always 3, never changes) -> print(3)

4. Shifted / combined value -> print a formula using i and j


Example: row i=1 -> "1 2", row i=2 -> "2 3"
-> print(i + j - 1)

Note: There is no single universal formula for 'what to print' - it requires reading the actual numbers in the pattern
each time, unlike the range formula which is always fixed.
11. Lists
A list stores MULTIPLE values together in one variable, instead of separate variables for each value.
fruit1 = "apple" # messy - one variable per value
fruit2 = "banana"

fruits = ["apple", "banana", "mango"] # clean - one list holds all values

Indexing (starts at 0, not 1)


colors = ["red", "green", "blue", "yellow"]
# 0 1 2 3

print(colors[0]) # red
print(colors[1]) # green
print(colors[-1]) # yellow (negative index = counts from the end)
print(len(colors)) # 4 (total number of items)

Note: colors[4] would crash - only indexes 0,1,2,3 exist in a 4-item list.

Modifying a list
fruits = ["apple", "banana"]

[Link]("mango") # adds to the END -> ['apple','banana','mango']


[Link](1, "grape") # inserts at index 1, pushes rest right
[Link]("banana") # removes by VALUE (searches for it)
[Link]() # removes the LAST item (no index given)
[Link](0) # removes item AT index 0
fruits[0] = "orange" # directly replaces whatever is at index 0

Note: append() adds to the end ONLY. insert(i, x) lets you choose the position. remove(x) deletes by value (crashes
if value not found). pop(i) deletes by index and RETURNS the removed item.
removed_item = [Link](0)
print(removed_item) # shows the actual value that was removed
print(fruits) # shows the list AFTER removal

Looping through a list


fruits = ["apple", "banana", "mango"]

for fruit in fruits: # gives you each VALUE directly, no range() needed
print(fruit)

for i in range(len(fruits)): # gives you the INDEX, use fruits[i] to get the value
print(f"Index {i}: {fruits[i]}")

Note: Use 'for item in list' when you only need the values. Use 'for i in range(len(list))' when you also need the
position/index.

Searching a list — manual method (loop + flag)


fruits = ["apple", "banana", "mango", "grape"]
search_item = input("Enter a fruit to search: ")
found = False
for i in fruits:
if i == search_item:
found = True
break # stop checking once found

if found:
print("Found")
else:
print("Not found")

Note: The 'found' flag survives after the loop ends, so you can check whether anything ever matched, even though
the loop itself is temporary.

Searching a list — shortcut using 'in'


if search_item in fruits:
print("Found")
else:
print("Not found")

Note: 'in' does exactly what the manual loop+flag does, but Python handles it internally in one line.

Replacing an item by VALUE (not just by index)


fruits = ["apple", "banana", "mango"]
if "banana" in fruits:
position = [Link]("banana") # find WHERE "banana" is
fruits[position] = "grape" # overwrite that exact position
print(fruits) # ['apple', 'grape', 'mango']

Processing all items — sum and smallest/largest


numbers = [12, 45, 7, 23, 56, 3]

total = 0
for i in numbers:
total += i
print(total) # 146

smallest = numbers[0] # assume first item is smallest, for now


for i in numbers:
if i < smallest:
smallest = i
print(smallest) # 3

Note: Built-in shortcuts exist too: sum(numbers), min(numbers), max(numbers) - we learned the manual version first
so you understand the mechanism underneath.
12. Dictionaries
A list stores items in ORDER, accessed by position. A dictionary stores items as KEY-VALUE pairs, accessed
by a name (key), not a position number.
student = {"name": "Attaullah", "age": 20, "city": "Swat"}

Accessing values
print(student["name"]) # Attaullah
print(student["age"]) # 20
# student["email"] would CRASH - key doesn't exist

Accessing safely with .get()


print([Link]("name")) # Attaullah (works same as [ ])
print([Link]("email")) # None (no crash)
print([Link]("email", "N/A")) # N/A (custom default instead of None)

Note: .get() never crashes even if the key is missing - safer than [ ] when you're not sure a key exists.

Adding and updating keys


student["email"] = "ali@[Link]" # adds a NEW key (didn't exist before)
student["age"] = 21 # UPDATES existing key (overwrites old value)

Note: Same syntax for add vs update - Python decides based on whether the key already exists.

Removing a key
del student["city"] # removes the key-value pair entirely

Looping through a dictionary


student = {"name": "Attaullah", "age": 20, "city": "Swat"}

for key in student: # gives you just the KEYS


print(key)

for key, value in [Link](): # gives you BOTH key and value together
print(f"{key}: {value}")

Building a dictionary from user input


person = {} # start EMPTY

person["name"] = input("What is your name? ")


person["age"] = int(input("What is your age? "))
person["favorite_color"] = input("What is your favorite color? ")

print(person)

Dictionary + conditional together


student = {"name": "Ali", "marks": 85}
if student["marks"] >= 90:
print("Grade A")
elif student["marks"] >= 80:
print("Grade B")
else:
print("Grade C")
13. Functions (def)
Functions let you write a block of logic ONCE and reuse it anywhere, instead of copy-pasting the same code
repeatedly.

Defining and calling


def greet():
print("Hello!")
print("Welcome to Python.")

greet() # defining a function does NOT run it - calling it does


greet() # can call it as many times as needed

Parameters (inputs to a function)


def greet(name):
print(f"Hello, {name}!")

greet("Ali") # Hello, Ali!


greet("Sara") # Hello, Sara!

def add_numbers(a, b): # multiple parameters


print(a + b)

add_numbers(5, 3) # 8

return vs print()
print() only DISPLAYS a value on screen. return actually HANDS BACK a value so it can be stored and
reused.
def add_print(a, b):
print(a + b) # just shows it

def add_return(a, b):


return a + b # sends it back

result = add_return(5, 3) # result = 8, usable afterward


print(result * 2) # 16 - works because result holds a real value

result2 = add_print(5, 3) # prints 8, but result2 is actually None!


print(result2 * 2) # ERROR - can't multiply None

Note: Any time a function's output needs to be stored, reused, or passed elsewhere, it MUST use return, not just
print().

Default parameter values


def greet(name="Guest"):
print(f"Hello, {name}!")

greet("Ali") # Hello, Ali!


greet() # Hello, Guest! (falls back to the default)
Taking user input inside a function
def get_student_info():
name = input("What is your name? ")
marks = int(input("What are your marks? "))
return name, marks # returning MULTIPLE values at once

student_name, student_marks = get_student_info() # unpacked into 2 variables


print(student_name)
print(student_marks)

Note: Forgetting 'return' means the function gives back None by default, even if it collected data internally with
input().

Functions + conditionals
def check_grade(marks):
if marks >= 90:
return "Grade A"
elif marks >= 80:
return "Grade B"
else:
return "Grade C"

result = check_grade(85)
print(result) # Grade B

Functions + loops + lists together


def print_grades(marks):
for i in marks:
if i >= 90:
print("Grade A")
elif i >= 80:
print("Grade B")
elif i >= 70:
print("Grade C")
else:
print("Fail")

print_grades([95, 82, 67, 45, 90])

Note: return inside a loop STOPS the entire function immediately on the first match. Use print() inside the loop
instead, if you need every item processed, not just the first.

Functions + dictionaries
def print_student_info(student):
for key, value in [Link]():
print(f"{key}: {value}")

student = {"name": "Ali", "age": 20, "city": "Swat"}


print_student_info(student)
Collecting user input into a list, inside a function
def get_numbers():
numbers = []
for i in range(5):
num = int(input("Enter a number: "))
[Link](num)
return numbers

def show_sum(numbers):
total = 0
for num in numbers:
total += num
print(total)

my_list = get_numbers()
show_sum(my_list)

Note: Splitting work into two small functions (one collects data, one processes it) is how real programs stay
organized - each function has ONE clear job.

Scope — local vs global variables


def my_function():
x = 10 # LOCAL - only exists inside this function
print(x)

my_function() # prints 10
print(x) # ERROR - x doesn't exist outside the function

name = "Ali" # GLOBAL - created outside any function


def show_name():
print(name) # can READ a global variable fine

show_name() # Ali

Note: Variables created inside a function are destroyed once the function finishes. To use a value outside a function,
it must be returned and stored in an outer variable.

Functions calling other functions


def get_marks():
marks = int(input("Enter marks: "))
return marks

def check_grade():
marks = get_marks() # calling ANOTHER function from inside this one
if marks >= 90:
print("Grade A")
else:
print("Grade B")

check_grade()
main() structure
Once a program has several functions, it's common to create ONE main() function that calls the others in order,
acting as a clear 'table of contents' for what the program does.
def get_two_numbers():
a = int(input("Enter first number: "))
b = int(input("Enter second number: "))
return a, b

def add_and_show(a, b):


total = a + b
print(f"The sum is {total}")

def main():
num1, num2 = get_two_numbers()
add_and_show(num1, num2)

main()

Note: You'll often see if __name__ == '__main__': main() at the bottom of real Python files - this means 'only run
main() if this file is run directly, not if it's imported elsewhere.' Deeper reasoning comes later when working with
multiple files/modules.
14. Coming Up Next
File handling (txt/csv/json), error handling (try/except), regular expressions, list/dict comprehensions, basic
OOP in Python (classes/objects - fast given existing C++ background), unit testing, then libraries (requests, os,
dotenv, AI SDKs for chatbots; pandas, numpy, matplotlib, streamlit for data analysis/dashboards), leading into
the first real mini-project.

You might also like