Python is a high-level, general-purpose programming language widely used for web development, software development, data science, artificial intelligence, automation, and more.
It is popular because of:
These qualities make Python the preferred choice for startups and enterprises and a highly sought-after skill in today’s job market.
Python 2 is a legacy version, while Python 3 introduced significant improvements and is the modern standard.
Key differences:
Python 2 reached end-of-life in 2020, so Python 3 is the standard for modern development.
Python has several built-in types, broadly divided into immutable and mutable:
Understanding these types helps developers write efficient and bug-free code.
A decorator is a design pattern that allows you to add new functionality to a function or class without modifying its code.
Example:
def decorator(func):
def wrapper():
print("Before function")
func()
print("After function")
return wrapper
@decorator
def say_hello():
print("Hello!")
say_hello()
Python uses pass-by-object-reference:
This behavior helps balance efficiency and flexibility in Python’s memory model.
Performance-wise, tuples are slightly more efficient due to immutability.
Python uses the try/except/finally structure for exception handling:
try:
x = 10 / 0
except ZeroDivisionError:
print("Division by zero is not allowed.")
finally:
print("Execution finished.")
This mechanism ensures robust error handling and resource management.
PEP 8 is the official style guide for Python code. It provides best practices for writing readable, consistent, and maintainable Python programs.
Following PEP 8 makes code cleaner, easier to review, and less error-prone. Many companies enforce it as part of their coding standards, and tools like flake8 or pylint automatically check PEP 8 compliance.
Python is primarily an interpreted language, but it also goes through a compilation step:
The __init__.py file serves two main purposes:
Since Python 3.3 (PEP 420), __init__.py is no longer strictly required (namespace packages). However, including it remains good practice for clarity, backward compatibility, and maintainability.
The difference lies in how nested objects are handled:
Use shallow copy for lightweight duplication and deep copy when full independence is required.
The enumerate() function adds a counter(index) to an iterable while looping.
Example without enumerate():
fruits = ["apple", "banana", "cherry"]
for i in range(len(fruits)):
print(i, fruits[i])
With enumerate():
for i, fruit in enumerate(fruits):
print(i, fruit)
These existed in Python 2:
In Python 3:
Object-Oriented Programming (OOP) is a paradigm that organizes code around objects (real-world entities) and classes (blueprints for objects). It promotes modularity, reusability, and clarity in software design.
OOP in Python:
Since everything in Python (even numbers and strings) is an object, it provides a natural fit for OOP, making it easy to model real-world problems.
Encapsulation means hiding internal details of a class while exposing only what’s necessary. Python supports encapsulation through naming conventions and access control:
While Python doesn’t enforce strict access restrictions like Java or C++, it relies on developer discipline and conventions.
Together, inheritance and polymorphism enable flexibility, extensibility, and reusable code structures in Python applications.
Python allows multiple inheritance, meaning a class can inherit from more than one parent class.
Example:
class A:
def greet(self): print("Hello from A")
class B:
def greet(self): print("Hello from B")
class C(A, B):
pass
c = C()
c.greet() # Follows Method Resolution Order (MRO)
Multiple inheritance is powerful but should be used carefully to avoid complexity and conflicts.
In Python, methods inside a class can be of three types:
Class Example:
def instance_method(self):
print("This is an instance method")
class Example:
count = 0
@classmethod
def increment(cls):
cls.count += 1
class Example:
@staticmethod
def utility():
print("This is a static method")
Difference:
A lambda function is a small, anonymous function in Python.
Example:
add = lambda x, y: x + y
print(add(5, 3)) # Output: 8
Use cases: short, throwaway functions in map(), filter(), sorted(), etc.
def greet(name):
return f"Hello {name}"
def say_hello(self):
return "Hello"
Difference:
#config.py
x = 10
import config
print(config.x) # 10
Best practice: avoid too many globals → use constants or pass variables explicitly.
A namespace is a mapping between names and objects.
Types of namespaces:
Purpose: prevent naming conflicts.
x = 10 # global
def func():
x = 5 # local
Ensures each identifier is unique in its context.
Python follows the LEGB rule for variable lookup:
Example:
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x)
inner()
outer() # local
Python searches in LEGB order until found.
Example:
def example(*args, **kwargs):
print(args) # tuple
print(kwargs) # dict
example(1, 2, 3, name="Alice", age=25)
# (1, 2, 3)
# {'name': 'Alice', 'age': 25}
Best practice: use *args for extra values, **kwargs for named options.
A metaclass in Python is a class of a class.
Example (default behavior):
class MyClass:
pass
print(type(MyClass)) # <class 'type'>
print(isinstance(MyClass, type)) # True
Custom Metaclass Example:
# Define a custom metaclass
class Meta(type):
def __new__(cls, name, bases, dct):
if "greet" not in dct:
raise TypeError("Class must define a 'greet' method")
return super().__new__(cls, name, bases, dct)
# This will work
class Person(metaclass=Meta):
def greet(self):
return "Hello"
# This will raise an error (no 'greet' method)
class Animal(metaclass=Meta):
pass
Key Uses of Metaclasses:
In practice, metaclasses are rarely needed — they’re powerful but add complexity. They are mainly used in frameworks like Django and SQLAlchemy.
The simplest and most Pythonic way is to use string slicing:
def reverse_string(s):
return s[::-1]
print(reverse_string("hello")) # Output: 'olleh'
A palindrome reads the same forward and backward.
def is_palindrome(s):
s = s.lower().replace(" ", "") # normalize
return s == s[::-1]
print(is_palindrome("mom")) # True
print(is_palindrome("Race car")) # True
Binary search works only on sorted lists.
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1
print(binary_search([1, 3, 5, 7, 9], 7)) # Output: 3
Iterative approach:
def factorial_iter(n):
result = 1
for i in range(1, n+1):
result *= i
return result
Recursive approach:
def factorial_rec(n):
if n == 0:
return 1
return n * factorial_rec(n - 1)
arr = [10, 25, 5, 89, 45]
print(max(arr)) # Output: 89
def find_max(arr):
maximum = arr[0]
for num in arr:
if num > maximum:
maximum = num
return maximum
def sort_numbers(nums):
return sorted(nums)
print(sort_numbers([4, 2, 9, 1])) # Output: [1, 2, 4, 9]
nums = [4, 2, 9, 1]
nums.sort()
print(nums) # [1, 2, 4, 9]
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
intersection = list(set(list1) & set(list2))
print(intersection) # [3, 4]
Quick method (unordered):
my_list = [1, 2, 2, 3, 4, 4, 5]
print(list(set(my_list))) # [1, 2, 3, 4, 5]
Preserve order:
def remove_duplicates(lst):
seen = []
for item in lst:
if item not in seen:
seen.append(item)
return seen
Using Python’s list:
stack = []
# Push
stack.append(10)
stack.append(20)
# Pop
print(stack.pop()) # 20
print(stack[-1]) # Peek → 10
Or wrap in a class:
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[-1]
def is_empty(self):
return len(self.items) == 0
Stack follows LIFO (Last-In, First-Out).
To read a file line by line and store it in a list, use Python’s built-in open() function along with a list comprehension.
Example:
# Reading file line by line into a list
with open("example.txt", "r") as file:
lines = [line.strip() for line in file]
print(lines)
There are multiple ways to merge dictionaries in Python:
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
dict1.update(dict2) # dict1 is modified
print(dict1) # {'a': 1, 'b': 3, 'c': 4}
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged_dict = {**dict1, **dict2} # creates new dict
print(merged_dict) # {'a': 1, 'b': 3, 'c': 4}
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 3, "c": 4}
merged_dict = dict1 | dict2
print(merged_dict) # {'a': 1, 'b': 3, 'c': 4}
Choose based on whether you want in-place modification (update) or a new dictionary (** or |).
You can count word frequency using a dictionary or Python’s built-in collections.Counter.
def word_frequency(text):
words = text.split()
freq = {}
for word in words:
word = word.lower() # normalize case
freq[word] = freq.get(word, 0) + 1
return freq
text = "Python is great and Python is easy"
print(word_frequency(text))
# {'python': 2, 'is': 2, 'great': 1, 'and': 1, 'easy': 1}
from collections import Counter
text = "Python is great and Python is easy"
freq = Counter(text.split())
print(freq)
# Counter({'Python': 2, 'is': 2, 'great': 1, 'and': 1, 'easy': 1})
Use Counter when you want quick frequency counts with extra utilities (like most_common).
You can find the second largest number by removing duplicates, sorting the list, and picking the second last element.
Example:
def second_largest(nums):
unique_nums = list(set(nums)) # remove duplicates
if len(unique_nums) < 2:
return "List must have at least two distinct numbers"
unique_nums.sort()
return unique_nums[-2]
print(second_largest([10, 20, 4, 45, 99])) # 45
Loop through the list and use the modulus operator % to classify numbers as even or odd. Example:
def count_even_odd(numbers):
even = odd = 0
for num in numbers:
if num % 2 == 0:
even += 1
else:
odd += 1
return even, odd
nums = [10, 21, 4, 45, 66, 93]
even, odd = count_even_odd(nums)
print("Even:", even, "Odd:", odd)
A prime number is greater than 1 and divisible only by 1 and itself. You only need to check divisibility up to the square root of the number.
Example:
import math
def is_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
print(is_prime(29)) # True
print(is_prime(10)) # False
Optimized check using square root.
Use list comprehension to check membership of elements from one list in the other.
Example:
def common_items(list1, list2):
return [item for item in list1 if item in list2]
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
print(common_items(list1, list2)) # [4, 5]
Simple and works well for small lists.
Example:
class Person:
def __init__(self, name):
self.name = name # instance attribute
p = Person("Alice")
print(p.name) # Output: Alice
x = 10 # global variable
def func():
global x
x += 5
_name = "Alice"
class Employee:
def __init__(self):
self.__salary = 5000
import module_name
from module_name import function_name
from package_name.module_name import function_name
Key point:
Type-checking stages:
Example:
x = 10 # x is integer
x = "Hello" # now x is string, no error
Example:
string = "This is a string."
# Split string by space
string_list = string.split(' ')
print(string_list) # Output: ['This', 'is', 'a', 'string.']
# Join list into string with space
print(' '.join(string_list)) # Output: This is a string.
Example:
numbers = [1, 2, 3]
it = iter(numbers)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# next(it) now raises Stop Iteration
| Feature | .py file | .pyc file |
|---|---|---|
| Content | Source code | Compiled bytecode |
| Creation | Written by programmer | Generated after importing .py |
| Execution | Compiled to bytecode first | Executed directly by VM |
| Purpose | Editable, human-readable | Saves compilation time |
Key point: .pyc files speed up execution by avoiding recompilation.
Copyright © 2023 - Proleed Academy | All Rights Reserved.