Most sections are linked to the relevant official Python documentation for further reading.
- Beginner Friendly Python Tutorial by official Python documentation: https://docs.python.org/3.13/tutorial/index.html#tutorial-index
- Python Standard Library reference: https://docs.python.org/3.13/library/index.html#library-index
In Python, indentation is not just for readability—it defines the structure and flow of the program.
- Significance: Unlike many other programming languages that use braces
{}
to denote code blocks, Python uses indentation.
Example:
# Correct indentation
if True:
print("This is indented correctly.")
print("Still within the if block.")
# Incorrect indentation (will raise an IndentationError)
if True:
print("This will cause an error.")
- Case Sensitivity: Python is case-sensitive (
Variable
,variable
, andVARIABLE
are different). - Comments: Use
#
for single-line comments and triple quotes'''
or"""
for multi-line comments/docstrings.
# This is a single-line comment
""" This is a multi-line
comment or docstring to define a function or class.
# even this is okay here because the # is just part of the string
"""
- Line Continuation: Two or more physical lines may be joined into logical lines using backslash characters (\)
if 1900 < year < 2100 and 1 <= month <= 12
and 1 <= day <= 31 and 0 <= hour < 24
and 0 <= minute < 60 and 0 <= second < 60: # Looks like a valid date
return 1
Line Continuation in Python is also possible inside parentheses ()
, brackets []
, and braces {}
without using the
backslash character.
my_list = [1, 2, 3, # a comment may appear here no problem
4, 5, 6]
my_dict = {'key1': 'value1', # another comment
'key2': 'value2',
'key3': 'value3'} # comment here too if you wish
- Reserved Keywords: Python has reserved words that cannot be used as identifiers.
False
await else import
pass
None
break except in raise
True
class finally is return
and continue
for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
Variables are used to store data. They are created by assigning a value to a name.
- Naming Rules:
- Must start with a letter (a-z, A-Z) or an underscore
_
. NOT by anything else. - Can contain letters, digits (0-9), and underscores.
- Case-sensitive.
- Should not be a reserved keyword.
- Must start with a letter (a-z, A-Z) or an underscore
Example:
# Valid variable names
my_variable = 5 # snake_case
_age1 = 30 # inside a class, _ prefix indicates a private variable or method
firstName = "John" # camelCase
# Invalid variable names (will raise SyntaxError)
2
nd_place = "Alice" # Starts with a digit
$price = 100 # variables cannot start with anything else than a letter or underscore
class = "Math" # 'class' is a reserved keyword
Python has several built-in data types that are fundamental to writing programs. The primary data types covered here are integers, floats, strings, and booleans.
-
Integers (
int
):- Whole numbers without a decimal point.
- Can be positive, negative, or zero.
- Python integers can be of arbitrary length, limited only by available memory.
- Immutable, meaning they cannot be changed once created. Pass by value, not by reference.
age = 25 score = -100 large_number = 123456789012345678901234567890
-
Floats (
float
):- Numbers with a decimal point.
- Can represent real numbers, including positive, negative, and zero.
- Large numbers are represented in scientific notation (e.g.,
1.0e-3
for 0.001). - Immutable, meaning they cannot be changed once created. Pass by value not by reference.
height = 5.9 temperature = -23.5
-
Complex Numbers (
complex
):- Numbers with a real and imaginary part.
- Written in the form
a + bj
, wherea
andb
are floats andj
is the imaginary unit.
z = 3 + 4j
4- Strings) (str
):
- Sequences of characters enclosed in single quotes
' '
or double quotes" "
or triple quotes''' '''
or""" """
. - Can include letters, numbers, symbols, and whitespace.
- Immutable, meaning they cannot be changed once created. If you need to modify a string, you create a new one.
name = "Alice"
message = 'Hello 2 World!'
multiline = '''This is a #42^5$
multi-line string'''
- Booleans (
bool
):- Represents one of two values:
True
orFalse
. Note, they are case-sensitive. - Here are most of the built-in objects considered false (example below)
constants defined to be false:
None
andFalse
. zero of any numeric type:0
,0.0
,0j
,Decimal(0)
,Fraction(0, 1)
empty sequences and collections:''
,()
,[]
,{}
,set()
,range(0)
- Represents one of two values:
is_student = True
list = []
print(bool(list)) # Output: False
int = 0
print(bool(int)) # Output: False
dict = None
print(bool(dict)) # Output: False
Additional Data Types (Brief definitions for now):
- Lists (
list
): Ordered, mutable collections. - Tuples (
tuple
): Ordered, immutable collections. - Dictionaries (
dict
): Unordered, mutable collections of key-value pairs. - Sets (
set
): Unordered collections of unique elements.
- Python uses dynamic typing, meaning that variable types are determined at runtime and can change as the program executes.
- Type Annotations are strongly recommended for better code readability and maintainability.
However, a common misconception is that type annotations make code faster or enforce type checking. They do not. They simply provide hints to developers and tools.
Read the Best Practices here: https://typing.readthedocs.io/en/latest/reference/best_practices.html
For a list of built-in types, examples on type hinting for functions and classes, check out this: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html
**Example:**
```python
# Variable initially assigned an integer
x = 10
print(x) # Output: 10
print(type(x)) # Output: <class 'int'>
# Reassigning the same variable to a string
x = "Ten"
print(x) # Output: Ten
print(type(x)) # Output: <class 'str'>
# Reassigning to a float
x = 10.5
print(x) # Output: 10.5
print(type(x)) # Output: <class 'float'>
3.1 Strings: F-Strings (Formatted String Literals)
Overview:
-
Introduced in Python 3.6: F-strings (
f
orF
) provide an efficient way to embed expressions directly within string literals. -
Syntax: Place expressions inside curly braces
{}
. Text outside braces is treated as literal. -
Basic String Interpolation:
- Embed variables directly into strings.
name = "Alice" age = 30 print(f"Name: {name}, Age: {age}") # Output: Name: Alice, Age: 30
-
Debugging with
=
:- Show both the expression and its evaluated value.
print(f"Debug: {age=}") # Output: Debug: age=30
-
Expressions Inside F-Strings:
- Use inline expressions like method calls or calculations.
name = "Alice" print(f"Uppercase: {name.upper()}") # Output: Uppercase: ALICE
-
Formatting Numbers:
- Use format specifiers to control the display of numbers appended by colon :
- For example,
:.2f
formats a float to two decimal places. - Format is defined by width, precision, and type (e.g.,
f
is float precision type) i.e.{value:width.precisiontype}
. - Width: How many characters the string should be. Padded with spaces if needed.
- Precision: Number of decimal places. rounded to fit.
Other useful format specifiers include
:b
for binary,:e
for scientific notation, and:%
for percentage.
height = 5.6789
print(f"Height: {height:.1f}") # Output: Height: 5.7
num = 19
print(f"{num:b}") # Output: 10011
percent = 65 / 100
print(f"{percent:%}") # Output: 65.000000%
print(f"{percent:.2%}") # Output: 65.00%
percent = 4531
print(f"{percent:.2e}") # Output: 4.53e+03
- Date Formatting:
- Use date format directives to display dates in a specific format:
- Directives: Special codes that start with
%
to represent date components.
Directive | Meaning | Example Output |
---|---|---|
%A |
Full weekday name | Friday |
%B |
Full month name | January |
%d |
Day of the month (zero-padded) | 27 |
%Y |
Year with century as decimal | 2017 |
%H |
Hour (24-hour clock) | 15 |
%M |
Minute | 45 |
%S |
Second | 30 |
from datetime import datetime
today = datetime(2017, 1, 27)
# print without directives
print(f"{today}") # Output: 2017-01-27 00:00:00
print(f"{today:%A, %B %d, %Y}") # Output: Friday, January 27, 2017
- Escaping Braces:
- Use double curly braces
{{
and}}
to include literal braces.
print(f"{{Hello}}") # Output: {Hello}
- Use double curly braces
3.2 Boolean Operations: and
, or
, not
Boolean operations in Python are used for logical decision-making. They operate on Boolean values (True
, False
)
x or y
- Result: Returns True if either
x
ory
is True, otherwise False. - Short-Circuit Behavior: Stops evaluating if
x
is True i.e. If x is True, the result is x, else y (this is often used in web development rendering)
- Result: Returns True if either
a = 0
b = 10
print(
a or b) # Output: 10 because a is evaluated as False (since its 0) and b is evaluated as True (since non-zero). Thus, the expression is (False OR True) = True
print(False or False) # Output: False
Use Case:
username = input("Enter username: ") or "Guest"
print(f"Welcome, {username}!") # Default to "Guest" if input() outputs empty string which is falsy.
-
x and y
- Result: Returns True if both
x
andy
are True, otherwise False. - Short-Circuit Behavior: Stops evaluating if
x
is falsy. i.e. If x is False, the result is x, else y.
You can use this short circuit for minor optimizations in your code. Such as if you have two conditions and the first one is more likely to be false, you can put it first or put the condition that's quicker to check first.
a = 0 b = 10 print(a and b) # Output: 0 print(b and a) # Output: 0
Use Case:
threshold = 50 score = 75 print("Pass" if score >= threshold and score <= 100 else "Invalid score")
- Result: Returns True if both
-
not x
- Result: Negates the value of
x
. Ifx
is True, returns False. Ifx
is False, returns True. - Lower Priority:
not
has lower precedence than comparison operators like<
,>
,==
, etc.
a = 0 b = 10 print(not a) # Output: True print(not b) # Output: False # Lower priority: "not a == b" will be evaluated as "not (a == b)"
Use Case:
is_logged_in = False if not is_logged_in: print("Please log in to continue.")
- Result: Negates the value of
3.3 Comparisons
Python supports eight comparison operations, all of which have the same priority.
Operation | Meaning |
---|---|
< |
Strictly less than |
<= |
Less than or equal |
> |
Strictly greater than |
>= |
Greater than or equal |
== |
Equal |
!= |
Not equal |
is |
Object identity |
is not |
Negated object identity |
Additional Operators
Operation | Meaning |
---|---|
in |
Checks if an element exists in an iterable (__contains__ method). |
not in |
Checks if an element does not exist in an iterable (__contains__ ). |
-
Basic Comparisons:
x = 5 y = 10 print(x < y) # Output: True print(x >= y) # Output: False
-
Equality and Identity:
a = [1, 2, 3] b = [1, 2, 3] c = a print(a == b) # Output: True (same content) print(a is b) # Output: False (different objects) print(a is c) # Output: True (same object)
-
Chained Comparisons:
x, y, z = 5, 10, 15 print(x < y <= z) # Output: True
-
Membership Testing:
nums = [1, 2, 3] print(2 in nums) # Output: True print(4 not in nums) # Output: True
- You may also say,
x < y <= z
which is equivalent tox < y and y <= z
. - The
in
andnot in
operators are supported by objects implementing the__contains__
method, such as lists, strings, and dictionaries.
Warning: Avoid using
is
andis not
to compare values. Use==
and!=
for value comparisons.is
andis not
are used to compare object identities.
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # Output: True
print(a is b) # Output: False since they are different objects in memory with different memory addresses
c = a
print(
a is c) # Output: True since they are the same object in memory which means they both point to the same memory location (same reference)
# Wrong use of is
x = 10
y = 10
print(x is y) # Output: True (unexpected result because of integer caching outside of the scope of this tutorial)
3.4 Operators on Numerical Types: int
and float
Python provides a variety of operations for working with numerical types such as int
and float
. Below is a breakdown
of these operations:
-
Addition (
x + y
): Adds two numbers.x = 10 y = 5 result = x + y print("Sum:", result) # Output: Sum: 15
-
Subtraction (
x - y
): Subtracts the second number from the first.x = 10 y = 5 result = x - y print("Difference:", result) # Output: Difference: 5
-
Multiplication (
x * y
): Multiplies two numbers.x = 10 y = 5 result = x * y print("Product:", result) # Output: Product: 50
-
Division (
x / y
): Divides the first number by the second and returns a floating-point result.x = 10 y = 4 result = x / y print("Quotient:", result) # Output: Quotient: 2.5 print(type(result)) # Output: <class 'float'>
- Floored Division (
x // y
): Returns the quotient of the division, rounded DOWN to the nearest integer.
left = 3
right = 4
mid = (left + right) // 2 # this is how mid is calculated in binary search
print(
f"Simple Integer Division: {(left + right) / 2}") # Output: 3.5 (in binary search we need an integer index not float like 3.5)
print("Floored Quotient:", mid) # Output: 3
- Modulus (
x % y
): Returns the remainder of the division.
x = 10
y = 3
print("Remainder:", x % y) # Output: Remainder: 1
print("Remainder:", y % x) # Output: Remainder: 3 (for any x % y, if x < y, the result is x)
# Example: Check if a number is even or odd
num = 10
print("Even") if num % 2 == 0 else print("Odd")
-
Absolute (
abs(x)
): Returns the absolute value ofx
.x = -10 print("Absolute Value:", abs(x)) # Output: Absolute Value: 10
- Convert to Integer (
int(x)
): Converts a float or string to an integer (truncating the fractional part for floats).
x = 3.7
print("Integer:", int(x)) # Output: Integer: 3
someString = "182"
print(int(someString)) # Output: 182
someString = " 182 " # spaces are ignored
print(int(someString)) # Output: 182
someString = "182.5" # decimal point is not allowed for int() conversion from string
print(int(someString)) # Output: ValueError: invalid literal for int() with base 10: ' 182.5 '
someString = "a2b2" # alphabets are not allowed
print(int(someString)) # Output: ValueError: invalid literal for int() with base 10: 'a2b2'
-
Convert to Float (
float(x)
): Converts an integer or string to a floating-point number.x = 3 print("Float:", float(x)) # Output: Float: 3.0
-
Power (
pow(x, y)
orx ** y
): Raisesx
to the power ofy
.x = 2 y = 3 print("Power (pow):", pow(x, y)) # Output: Power (pow): 8 print("Power (**):", x ** y) # Output: Power (**): 8 print(pow(0,0)) # Output: 1 (0^0 = 1) a common mathematical convention
-
Divmod (
divmod(x, y)
): divmod(x,y) = (x // y, x % y). Returns a tuple containing the floored quotient and remainder of the division.x = 10 y = 3 print("Divmod:", divmod(x, y)) # Output: Divmod: (3, 1) print("Divmod:", divmod(y, x)) # Output: Divmod: (0, 3)
- The modulus operator (
%
) and floored division work consistently with negative numbers. - Conversion from
float
toint
truncates the decimal part. float()
accepts strings like "+inf" or "-nan" to represent infinity or NaN.
3.5 String Methods
Python provides many built-in methods to work with strings. Below are some of them but checkout the documentation for a complete list.
-
str.upper()
: Converts all characters in the string to uppercase.text = "hello world" print(text.upper()) # Output: HELLO WORLD
-
str.lower()
: Converts all characters in the string to lowercase.text = "HELLO WORLD" print(text.lower()) # Output: hello world
-
str.capitalize()
: Capitalizes the first character of the string.text = "hello world" print(text.capitalize()) # Output: Hello world
-
str.title()
: Capitalizes the first character of each word.text = "hello world" print(text.title()) # Output: Hello World
-
str.find(substring)
: Returns the index of the first occurrence ofsubstring
in the string, or-1
if not found.text = "hello world" print(text.find("world")) # Output: 6
-
str.replace(old, new, count=-1)
: Replaces all occurrences ofold
withnew
.
Python 3.13+ allows you to use
count
parameter to specify the number of occurrences to replace. If count is given, only the first count occurrences are replaced. If count is not specified or -1, then all occurrences are replaced.
text = "X is crazy, X is a genius, X is a fool, X is a hero"
print(text.replace("X", "Z", count=2)) # Output: Z is crazy, Z is a genius, X is a fool, X is a hero
text = "X is crazy, X is a genius, X is a fool, X is a hero"
print(text.replace("X", "Z")) # Output: Z is crazy, Z is a genius, Z is a fool, Z is a hero
str.split(delimiter)
: Splits the string into a list of substrings based on the specifieddelimiter
.
text = "apple,banana,cherry"
print(text.split(",")) # Output: ['apple', 'banana', 'cherry']
# Converting a string to a list of characters
print(list("apple")) # Output: ['a', 'p', 'p', 'l', 'e']
-
delimiter.join(iterable)
: Joins elements of an iterable (e.g., list) into a single string using thedelimiter
.fruits = ["apple", "banana", "cherry"] print(",".join(fruits)) # Output: apple,banana,cherry
-
str.strip()
: Removes leading and trailing whitespace.text = " hello world " print(text.strip()) # Output: hello world
-
str.lstrip()
: Removes leading whitespace.text = " hello world" print(text.lstrip()) # Output: hello world
-
str.rstrip()
: Removes trailing whitespace.text = "hello world " print(text.rstrip()) # Output: hello world
-
str.isdigit()
: ReturnsTrue
if all characters in the string are digits.text = "12345" print(text.isdigit()) # Output: True
-
str.isalpha()
: ReturnsTrue
if all characters in the string are alphabetic.text = "hello" print(text.isalpha()) # Output: True
-
str.isalnum()
: ReturnsTrue
if all characters in the string are alphanumeric.text = "hello123" print(text.isalnum()) # Output: True
Brief Review of Objects
-
Everything is an Object
All data in Python (e.g., integers, strings, lists, functions) is represented as objects. This abstraction allows uniformity in how data and operations are managed.x = 10 # x is an Object of type int print(type(x)) # Output: <class 'int'>
-
Identity, Type, and Value
Each object in Python has:- Identity: A unique identifier, equivalent to the object's memory address.
- Type: Defines the operations an object supports.
- Value: The data the object represents.
x = 10 print(id(x)) # Unique identity print(type(x)) # Object type print(x) # Object value
-
Immutability vs Mutability
- Immutable objects: Their value cannot change (e.g.,
int
,float
,str
,tuple
). - Mutable objects: Their value can change (e.g.,
list
,dict
).
- Immutable objects: Their value cannot change (e.g.,
# Immutable example
x = 10
print(id(x)) # example output: 171357067490663 (varies for each run)
x = x + 1 # New object created with new id
print(id(x)) # example output: 171357067490695 (varies for each run)
# Mutable example
lst = [1, 2, 3]
print(id(lst)) # example output: 928137487123 (varies for each run)
lst.append(4) # Modified in place
print(id(lst)) # example output: 928137487123 (same as above)
-
The
is
Operator andid()
Functionis
: Checks if two variables point to the same object.id()
: Returns the identity of an object.
a = 10 b = 10 print(a is b) # Output: True (same object) print(id(a), id(b)) # Same ID
-
Garbage Collection
Objects are automatically managed by Python's garbage collector. When no references to an object exist, it becomes eligible for cleanup. -
Containers and References
- Containers (e.g.,
list
,dict
) hold references to other objects. - The container's value reflects the references, not the actual data.
- Containers (e.g.,
# Create a mutable object (list) and a container (list of lists)
inner_list = [1, 2, 3]
outer_list = [inner_list] # outer_list holds a reference to inner_list
# Modify the referenced object
inner_list.append(4)
# Both inner_list and outer_list reflect the change
print(inner_list) # Output: [1, 2, 3, 4]
print(outer_list) # Output: [[1, 2, 3, 4]]
- Mutable Objects in Immutable Containers
Immutable containers (e.g.,tuple
) can reference mutable objects, and those referenced objects can change.
twice = ([], "abc")
twice[0].append(5)
print(twice)
# However, if you try to reassign the tuple, it will raise an error
twice[0] = [1,2,3] # output: TypeError: 'tuple' object does not support item assignment
twice[1] = "def" # output: TypeError: 'tuple' object does not support item assignment
-
Shared References
For mutable objects, multiple variables can reference the same object, leading to shared state.a = [1, 2, 3] b = a # Both reference the same list b.append(4) print(a) # Output: [1, 2, 3, 4]
-
Object Finalization
Objects managing external resources (e.g., files) should explicitly release them using methods likeclose()
. Thewith
statement ensures proper cleanup.with open('file.txt', 'w') as file: file.write('Hello, world!') # File automatically closed
-
Optimization for Immutable Objects Python may reuse objects for immutable types with the same value to save memory (Integer Caching).
Important: Never rely on this behavior for program logic, as it's an implementation detail that can vary.
a = 256 b = 256 print(a is b) # Output: True (same memory address for small integers due to integer caching) x = 257 y = 257 print(x is y) # Output: False (different memory addresses for larger integers)
Note: Integer caching typically applies to integers between -5 and 256. Behavior may differ in various Python implementations.
If you are more interested here is some information:
Python implements integer caching with the following rules:
-
By default, Python caches integers between -5 and 256 (inclusive)
-
When using integers within this range, variables pointing to the same value will reference the same object in memory
-
The caching is done at interpreter startup for performance optimization, since these numbers are frequently used
-
The REPL (interactive interpreter) strictly follows the -5 to 256 range
-
The compiler may optimize and cache additional numbers outside this range in certain situations
-
This caching behavior applies to integers only, though some implementations may also cache certain float values