Table of contents
- Introduction
- Variables & Datatypes
- Operators
- Escape Characters
- Input and Output
- Control Statements
- Loops
- Strings and Characters
- Lists, Tuples and Dictionaries
- Functions
- Object Oriented Programming (OOP)
- Classes and Objects
- __init__() Method and 'self ' Parameter
- Multiple Constructors
- Encapsulation
- Public And Private Methods
- Inheritance
- Getter and Setter
- Creating And Importing Module
- Creating User-Defined Module
- Multiple Inheritance
- super() Function
- Composition
- Aggregation
- Abstract Classes
- import & from
- Operator Overloading
- Naming Convention
- Errors And Exceptions Handling
- __name__ == "__main__"
- File Handling
- Python Package Management System
- Recursion
- Map, Filter and Reduce
- List Comprehension
- Regular Expressions
- Decorators
- Logging
- Date and Time
Introduction
Python is a high-level, interpreted, and general-purpose programming language created by Guido van Rossum and first released in 1991. Python emphasizes readability and simplicity, which allows developers to write clear, concise code. It is a versatile language, suitable for a wide range of applications, such as web development, data analysis, artificial intelligence, machine learning, automation, and more.
Some key features of Python include:
Readability: Python's syntax is designed to be easy to read and understand, using indentation to define code blocks instead of braces or other special characters.
Dynamically-typed: Variables in Python are not explicitly typed, meaning you don't need to declare a variable's data type when you create it. Python determines the data type at runtime based on the value assigned to the variable.
Cross-platform compatibility: Python is available for various platforms, such as Windows, macOS, Linux, and others. Python code can be run on any platform with a compatible Python interpreter.
Extensive Standard Library: Python has a rich standard library, providing a wide range of functionality, including file handling, regular expressions, networking, and more. This allows developers to accomplish many tasks without needing to install additional libraries.
Third-party Libraries: Python has a large ecosystem of third-party libraries, covering various domains like web development (Django, Flask), data analysis (Pandas, NumPy), machine learning (TensorFlow, scikit-learn), and many more.
Community: Python has a large and active community of developers, which means you can find a wealth of resources, tutorials, and support online.
Installation
To install Python on Windows and macOS, follow these steps:
Windows:
Visit the Python official website: https://www.python.org/downloads/
Download the latest version of Python for Windows by clicking on the "Download Python x.x.x" button (where x.x.x is the latest version number).
Once the installer is downloaded, locate the installer file (usually in your "Downloads" folder) and double-click it to run the installation.
In the installer window, check the box that says "Add Python x.x to PATH" to automatically add Python to your system's PATH variable. This will make it easier to run Python from the command line.
Choose "Customize installation" if you want to change any settings or install additional features, or "Install Now" for the default settings. We recommend the "Install Now" option for most users.
Wait for the installation to finish, and then click on "Close."
macOS:
Visit the Python official website: https://www.python.org/downloads/
Download the latest version of Python for macOS by clicking on the "Download Python x.x.x" button (where x.x.x is the latest version number).
Once the installer is downloaded, locate the installer file (usually in your "Downloads" folder) and double-click it to run the installation.
In the installer window, follow the on-screen instructions to complete the installation process. The installer will guide you through the necessary steps.
When the installation is complete, click on "Close."
To verify that Python is installed correctly on either Windows or macOS, open a terminal or command prompt window and type the following command:
python --version
If the installation was successful, you should see the Python version number displayed in the output.
Note that macOS comes with a pre-installed version of Python, which is usually Python 2.x. It is still recommended to install the latest version of Python 3.x following the steps above, as Python 2.x is no longer supported or maintained.
Writing Python Code For First Time
To run Python in Immediate mode, also known as the Python Interactive Shell or REPL (Read-Eval-Print Loop), follow these steps:
Windows:
Open Command Prompt (CMD).
Type
python
and press Enter. You should see the Python version and the ">>>" prompt indicating that you are in the Python Interactive Shell.
Mac:
Open Terminal.
Type
python3
and press Enter. You should see the Python version and the ">>>" prompt indicating that you are in the Python Interactive Shell.
Now you can directly enter Python code, and it will be executed immediately. For example, to print "Hello, World!", just type:
print("Hello, World!")
Press Enter, and you'll see "Hello, World!" printed in the terminal or command prompt.
To exit the Python Interactive Shell, type exit()
or press Ctrl + D
on Mac or Ctrl + Z
followed by Enter on Windows.
Variables & Datatypes
In Python, variables are used to store values, which can be of different data types. A variable is a name that represents a value stored in the memory. The data type of a variable determines the kind of value it can hold and the operations that can be performed on it.
Here's a detailed explanation of variables and data types in Python:
Variables
Variables are created when you assign a value to them, using the assignment operator (=).
Variable names should be descriptive and follow these naming conventions:
They can contain letters, numbers, and underscores.
They must start with a letter or an underscore.
They are case-sensitive (e.g.,
my_variable
andMy_Variable
are different variables).
Python is a dynamically typed language, which means you don't need to declare the data type of a variable when you create it. The data type is inferred automatically based on the assigned value.
Example:
x = 10 # Integer variable
name = "John" # String variable
pi = 3.14159 # Float variable
Data Types
Python has several built-in data types, including:
Numeric Data Types:
Integer (
int
): Represents whole numbers, both positive and negative. Example:x = 42
Float (
float
): Represents real numbers, including decimal and fractional values. Example:y = 3.14
Complex (
complex
): Represents complex numbers, consisting of a real and an imaginary part. Example:z = 2 + 3j
Sequence Data Types:
String (
str
): Represents a sequence of characters enclosed in single (' '
) or double (" "
) quotes. Example:name = "Alice"
List (
list
): Represents an ordered, mutable collection of values, enclosed in square brackets ([ ]
). Example:colors = ["red", "green", "blue"]
Tuple (
tuple
): Represents an ordered, immutable collection of values, enclosed in parentheses (( )
). Example:point = (3, 4)
Mapping Data Type:
- Dictionary (
dict
): Represents an unordered collection of key-value pairs, enclosed in curly braces ({ }
). Example:person = {"name": "Emma", "age": 30}
- Dictionary (
Set Data Types:
Set (
set
): Represents an unordered, mutable collection of unique values, enclosed in curly braces ({ }
). Example:fruits = {"apple", "banana", "orange"}
Frozen Set (
frozenset
): Represents an unordered, immutable collection of unique values. Example:immutable_fruits = frozenset(["apple", "banana", "orange"])
Boolean Data Type:
- Boolean (
bool
): Represents two values,True
andFalse
, often used to indicate the result of a comparison or logical operation. Example:is_active = True
- Boolean (
To determine the data type of a variable, you can use the type()
function:
x = 10
print(type(x)) # Output: <class 'int'>
Python allows you to convert between data types using type casting, for example:
x = 5.5
y = int(x) # Converts the float value 5.5 to the integer value 5
print(y) # Output: 5
Keep in mind that when casting between data types, you may lose information, as in the example above.
Literals and Identifiers
Literals and identifiers are fundamental elements in programming languages, including Python. Here's an explanation of each term:
Literals
Literals are fixed values or constants used in programming languages to represent values of different data types. In Python, literals can be categorized into several types:
Numeric literals:
Integer literals: Whole numbers, e.g.,
42
,-3
,0
Floating-point literals: Real numbers with decimal points, e.g.,
3.14
,-0.5
,1.0
Complex literals: Complex numbers, e.g.,
2+3j
,4-5j
String literals:
Single-quoted strings: Enclosed in single quotes, e.g.,
'Hello, world!'
Double-quoted strings: Enclosed in double quotes, e.g.,
"Python is awesome!"
Triple-quoted strings: Enclosed in triple single or double quotes (used for multiline strings), e.g.,
'''This is a multiline string.'''
or"""This is another multiline string."""
Boolean literals:
True
andFalse
representing the boolean values of truth and falsehood, respectively.
Special literals:
None
: Represents the absence of a value or a null value. It is often used to indicate that a variable has no value assigned.
Identifiers
Identifiers are names used to identify variables, functions, classes, modules, or other objects in a program. In Python, identifiers must follow these rules:
They can consist of letters (a-z, A-Z), digits (0-9), or underscores (_).
They must start with a letter or an underscore, but not a digit.
They are case-sensitive, meaning that
myVariable
andmyvariable
are considered different identifiers.Python has reserved words, also known as keywords, which cannot be used as identifiers. Some examples of reserved words are:
if
,else
,while
,for
,def
,class
, etc.
Examples of valid identifiers in Python:
my_variable = 10
userName = "Alice"
_list_items = [1, 2, 3]
In these examples, my_variable
, userName
, and _list_items
are identifiers representing variables in the program.
Reserve Words
Reserved words, also known as keywords, are words that have special meanings and uses in a programming language. They are predefined and cannot be used as identifiers (variable names, function names, class names, etc.) in your program. Python has a specific set of reserved words that serve various purposes, such as defining the structure of the code, control flow, and data manipulation.
Here are some examples of reserved words in Python, along with their uses:
if
,elif
, andelse
: Used for conditional statements to control the flow of the program based on certain conditions.
age = 18
if age >= 18:
print("You are an adult.")
else:
print("You are not an adult.")
while
andfor
: Used for loops, which allow you to execute a block of code repeatedly based on a condition or a specific range.
# While loop example
count = 0
while count < 5:
print(count)
count += 1
# For loop example
for i in range(5):
print(i)
def
: Used to define functions, which are reusable blocks of code that perform a specific task.
def greet(name):
print("Hello, " + name)
greet("Alice")
class
: Used to define classes, which are templates for creating objects in object-oriented programming.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print("Woof, woof!")
my_dog = Dog("Buddy", 3)
my_dog.bark()
import
: Used to import external modules or libraries into your program.
import math
print(math.sqrt(16))
try
,except
,finally
, andraise
: Used for error and exception handling to manage unexpected events during the execution of your program.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")
finally:
print("This will always be executed.")
lambda
: Used to create small, anonymous functions that are typically used for short operations.
square = lambda x: x * x
print(square(5)) # Output: 25
These are just a few examples of reserved words in Python. To view the complete list of reserved words, you can use the following code:
import keyword
print(keyword.kwlist)
Remember that reserved words are case-sensitive and cannot be used as identifiers in your program. Using them as such will result in syntax errors.
Operators
Operators in Python are symbols that perform specific operations on operands (variables or values). Python supports various types of operators, such as arithmetic, relational, logical, assignment, bitwise, and more.
Here's a detailed explanation of different types of operators in Python, along with examples:
Arithmetic operators: Perform arithmetic operations like addition, subtraction, multiplication, division, etc.
Addition (
+
): Adds two operands. Example:x = 5 + 3 # x will be 8
Subtraction (
-
): Subtracts the second operand from the first. Example:x = 5 - 3 # x will be 2
Multiplication (
*
): Multiplies two operands. Example:x = 5 * 3 # x will be 15
Division (
/
): Divides the first operand by the second. Example:x = 5 / 3 # x will be approximately 1.6667
Modulus (
%
): Returns the remainder of the division. Example:x = 5 % 3 # x will be 2
Exponentiation (
**
): Raises the first operand to the power of the second. Example:x = 5 ** 3 # x will be 125
Floor division (
//
): Performs division and returns the largest integer less than or equal to the division result. Example:x = 5 // 3 # x will be 1
Relational (comparison) operators: Compare the values of two operands and return a boolean result (
True
orFalse
).Equal to (
==
): ReturnsTrue
if the operands are equal. Example:x = (5 == 3) # x will be False
Not equal to (
!=
): ReturnsTrue
if the operands are not equal. Example:x = (5 != 3) # x will be True
Greater than (
>
): ReturnsTrue
if the first operand is greater than the second. Example:x = (5 > 3) # x will be True
Less than (
<
): ReturnsTrue
if the first operand is less than the second. Example:x = (5 < 3) # x will be False
Greater than or equal to (
>=
): ReturnsTrue
if the first operand is greater than or equal to the second. Example:x = (5 >= 3) # x will be True
Less than or equal to (
<=
): ReturnsTrue
if the first operand is less than or equal to the second. Example:x = (5 <= 3) # x will be False
Logical operators: Perform logical operations like AND, OR, and NOT, typically used with boolean values.
AND (
and
): ReturnsTrue
if both operands are true. Example:x = (True and False) # x will be False
OR (
or
): ReturnsTrue
if at least one of the operands is true. Example:x = (True or False) # x will be True
NOT (
not
): ReturnsTrue
if the operand is false, andFalse
if it is true. Example:x = not True # x will be False
Assignment operators: Assign values to variables, often used to update the value of a variable.
Assignment (
=
): Assigns the value of the right operand to theleft operand. Example:x = 5 # x will be 5
Add and assign (
+=
): Adds the right operand to the left operand and assigns the result to the left operand. Example:x = 5; x += 3 # x will be 8
Subtract and assign (
-=
): Subtracts the right operand from the left operand and assigns the result to the left operand. Example:x = 5; x -= 3 # x will be 2
Multiply and assign (
*=
): Multiplies the right operand by the left operand and assigns the result to the left operand. Example:x = 5; x *= 3 # x will be 15
Divide and assign (
/=
): Divides the left operand by the right operand and assigns the result to the left operand. Example:x = 5; x /= 3 # x will be approximately 1.6667
Modulus and assign (
%=
): Divides the left operand by the right operand, and assigns the remainder to the left operand. Example:x = 5; x %= 3 # x will be 2
Exponentiation and assign (
**=
): Raises the left operand to the power of the right operand and assigns the result to the left operand. Example:x = 5; x **= 3 # x will be 125
Floor division and assign (
//=
): Performs floor division on the left operand by the right operand and assigns the result to the left operand. Example:x = 5; x //= 3 # x will be 1
Bitwise operators: Perform operations on integers at the binary (bit) level.
Bitwise AND (
&
): Performs a bitwise AND operation on the operands. Example:x = 5 & 3 # x will be 1 (binary: 0101 & 0011 = 0001)
Bitwise OR (
|
): Performs a bitwise OR operation on the operands. Example:x = 5 | 3 # x will be 7 (binary: 0101 | 0011 = 0111)
Bitwise XOR (
^
): Performs a bitwise XOR operation on the operands. Example:x = 5 ^ 3 # x will be 6 (binary: 0101 ^ 0011 = 0110)
Bitwise NOT (
~
): Performs a bitwise NOT operation on the operand (inverts the bits). Example:x = ~5 # x will be -6 (binary: ~0101 = 1010)
Left shift (
<<
): Shifts the bits of the left operand to the left by the number of positions specified by the right operand. Example:x = 5 << 2 # x will be 20 (binary: 0101 << 2 = 10100)
Right shift (
>>
): Shifts the bits of the left operand to the right by the number of positions specified by the right operand. Example:x = 5 >> 2 # x will be 1 (binary: 0101 >> 2 = 0001)
Membership operators: Test whether a value is a member of a sequence, such as strings, lists, or tuples.
In (
in
): ReturnsTrue
if the specified value is found in the sequence. Example:x = "p" in "Python" # x will be True
Not in (
not in
): ReturnsTrue
if the specified value is not found in the sequence. Example:x = "z" not in "Python" # x will be True
Identity operators: Compare the memory locations of two objects to determine if they are the same.
Is (
is
): ReturnsTrue
if both variables refer to the same object. Example:a = [1, 2, 3] b = a x = (a is b) # x will be True
Is not (
is not
): ReturnsTrue
if both variables do not refer to the same object. Example:a = [1, 2, 3] b = [1, 2, 3] x = (a is not b) # x will be True
Note that when using identity operators, it is important to understand the difference between comparing object references (using is
and is not
) and comparing object values (using ==
and !=
). The identity operators compare object references, whereas the comparison operators compare object values.
Escape Characters
Escape characters are special characters in string literals that are preceded by a backslash (\
). They allow you to include characters that are difficult or impossible to type directly, such as newline characters, tab characters, or quotes within a string. Here are some commonly used escape characters in Python, along with examples:
Newline (
\n
): Inserts a newline in the string.print("Hello,\nWorld!") # Output: # Hello, # World!
Tab (
\t
): Inserts a horizontal tab in the string.print("Hello,\tWorld!") # Output: Hello, World!
Backslash (
\\
): Inserts a single backslash in the string. This is useful when you need to include a backslash in your string, since the backslash character is used for escaping.print("Hello,\\World!") # Output: Hello,\World!
Single quote (
\'
): Inserts a single quote in the string. This is useful when you need to include a single quote within a single-quoted string.print('It\'s a beautiful day!') # Output: It's a beautiful day!
Double quote (
\"
): Inserts a double quote in the string. This is useful when you need to include a double quote within a double-quoted string.print("She said, \"Hello, World!\"") # Output: She said, "Hello, World!"
ASCII Bell (
\a
): Generates an ASCII bell character, which can be used to trigger a sound (such as a beep) on some systems. Note that this may not work on all systems or in all environments.print("\a")
Carriage return (
\r
): Inserts a carriage return character, which moves the cursor to the beginning of the current line. This can be used to overwrite text on the same line.print("Hello, World!\rOverwritten") # Output: Overwritten World!
These are some examples of escape characters in Python. By using escape characters, you can include special characters in your strings that would otherwise be difficult or impossible to type directly.
Input and Output
In Python, input and output operations are essential for interacting with users or external sources, as well as displaying information to the user.
Input: The
input()
function is used to get input from the user in the form of a string. It accepts an optional argument, which is the prompt string displayed to the user before waiting for their input. The user's input is then stored in a variable, typically for further processing or use in the program.Example:
name = input("Enter your name: ") print("Hello, " + name + "!")
In this example, the user is prompted to enter their name, which is then stored in the
name
variable. The program then greets the user using their name.Note that the
input()
function always returns a string. If you need to use the input as a different data type, such as an integer or a float, you'll need to convert the input using the appropriate functions, likeint()
orfloat()
.Example:
age = int(input("Enter your age: ")) print("You are", age, "years old.")
Output: The
print()
function is used to display information to the user. It can accept multiple arguments separated by commas, which will be concatenated as a single string with spaces between them. You can also use string formatting to create more complex output strings.Example:
name = "Alice" age = 30 print("Name:", name, "Age:", age) # Output: Name: Alice Age: 30
String formatting can be done using f-strings (available in Python 3.6 and later) or the
str.format()
method.Example using f-strings:
name = "Alice" age = 30 print(f"Name: {name}, Age: {age}") # Output: Name: Alice, Age: 30
Example using
str.format()
:name = "Alice" age = 30 print("Name: {}, Age: {}".format(name, age)) # Output: Name: Alice, Age: 30
The
print()
function also supports additional arguments, such assep
(to specify the separator between multiple arguments) andend
(to specify the string added at the end of the line, which is a newline character\n
by default).Example using
sep
andend
:print("Name:", name, "Age:", age, sep=" | ", end=".") # Output: Name: | Alice | Age: | 30.
In summary, the input()
function is used to get user input, while the print()
function is used to display information to the user. These functions are essential for user interaction and presenting data to the user in a readable format.
Control Statements
Conditional statements in Python are used to control the flow of a program by executing specific code blocks based on conditions being met. The primary conditional statements in Python are if
, elif
, and else
.
if / else / elif Statements
if
statement: Theif
statement is used to test a condition. If the condition is true, the code block under theif
statement will be executed.Example:
age = 18 if age >= 18: print("You are eligible to vote.")
In this example, since
age
is equal to 18, the conditionage >= 18
is true, so the message "You are eligible to vote." will be printed.else
statement: Theelse
statement is used to define a code block that will be executed when the condition in theif
statement is not true.Example:
age = 16 if age >= 18: print("You are eligible to vote.") else: print("You are not eligible to vote.")
In this example, since
age
is less than 18, the conditionage >= 18
is false, so the message "You are not eligible to vote." will be printed.elif
statement: Theelif
(short for "else if") statement is used to test multiple conditions in sequence. If the condition in anif
orelif
statement is true, the corresponding code block will be executed, and the rest of theelif
andelse
blocks will be skipped.Example:
age = 18 if age < 13: print("You are a child.") elif age >= 13 and age < 18: print("You are a teenager.") else: print("You are an adult.")
In this example, the first condition
age < 13
is false, so the second conditionage >= 13 and age < 18
is checked. Since this condition is also false, the code block under theelse
statement will be executed, printing "You are an adult."
These conditional statements (if
, elif
, and else
) allow you to create complex branching logic in your Python programs based on various conditions. They are essential for controlling the flow of a program and enabling it to make decisions based on input data or other conditions.
Indentation
In Python, indentation is used to define the structure of code blocks, such as those within loops, conditional statements, functions, and classes. Unlike many other programming languages that use curly braces ({}
) or other symbols to define code blocks, Python relies solely on indentation.
A consistent number of spaces or a single tab is used to create an indentation level. Most Python developers prefer using 4 spaces for each indentation level, and this is the recommended convention according to the Python style guide (PEP 8). Mixing tabs and spaces is discouraged, as it can lead to confusing indentation levels and syntax errors.
Here's an example demonstrating the use of indentation in Python:
def greet(name):
if name == "Alice":
message = "Hello, Alice!"
else:
message = f"Hello, {name}!"
print(message)
greet("Bob")
In this example, the function greet
has a code block defined by one level of indentation. Inside the function, there are two conditional statements (if
and else
), each with their own indented code block. The print(message)
statement is at the same indentation level as the if
statement, indicating that it is part of the greet
function but not part of the if
or else
code blocks.
If the indentation were incorrect or inconsistent, it would lead to either syntax errors or unexpected behavior. For example:
def greet(name):
if name == "Alice": # IndentationError: expected an indented block
message = "Hello, Alice!"
else:
message = f"Hello, {name}!"
In this case, Python raises an IndentationError
because it expects an indented code block after the function definition.
In summary, indentation is crucial for defining the structure of code blocks in Python. It is important to maintain consistent indentation levels throughout your code to avoid errors and ensure the correct behavior of your program.
Nested Statements
Nested if-else
statements occur when you have one or more if
, elif
, or else
statements inside another if
, elif
, or else
block. This is used when you need to check multiple conditions in a hierarchical manner or when you want to perform different actions based on a combination of conditions.
Here's an example demonstrating the use of nested if-else
statements in Python:
age = 25
country = "USA"
if age >= 18:
if country == "USA":
print("You are eligible to vote in the USA.")
elif country == "India":
print("You are eligible to vote in India.")
else:
print("You are eligible to vote, but your country is not specified.")
else:
print("You are not eligible to vote.")
In this example, we have a nested if-elif-else
block inside the outer if
block. The outer if
block checks whether the person is 18 or older. If this condition is met, the nested if-elif-else
block checks the country
variable to determine where the person is eligible to vote. If the person is not 18 or older, the else
block of the outer if
statement is executed, and the person is informed that they are not eligible to vote.
Here's another example using nested if-else
statements:
score = 75
if score >= 50:
if score >= 90:
grade = "A"
elif score >= 70:
grade = "B"
else:
grade = "C"
else:
grade = "F"
print(f"Your grade is {grade}.")
In this example, the outer if
block checks whether the score is 50 or higher. If this condition is met, the nested if-elif-else
block assigns a grade (A, B, or C) based on the score. If the score is less than 50, the else
block of the outer if
statement is executed, assigning a grade of "F". Finally, the grade is printed.
Nested if-else
statements can be useful for creating complex decision-making logic in your programs. However, it's important to keep the code readable and avoid excessive nesting levels, as it can make your code difficult to understand and maintain.
Loops
Loops are a fundamental programming concept that allows you to execute a block of code repeatedly based on a condition or a sequence of values. Python provides two types of loops: for
loops and while
loops.
for & while Loop
for
loop: Thefor
loop in Python is used to iterate over a sequence (such as a list, tuple, string, or any other iterable object). The loop continues until all elements in the sequence have been processed.Example:
fruits = ["apple", "banana", "orange"] for fruit in fruits: print(fruit) # Output: # apple # banana # orange
In this example, the
for
loop iterates over thefruits
list and prints each fruit on a separate line.You can also use the
range()
function to generate a sequence of numbers for thefor
loop to iterate over:for i in range(5): print(i) # Output: # 0 # 1 # 2 # 3 # 4
In this example, the
for
loop iterates over the numbers 0 to 4 (inclusive) generated by therange()
function and prints each number on a separate line.while
loop: Thewhile
loop in Python is used to repeatedly execute a block of code as long as a given condition is true. Once the condition becomes false, the loop stops.Example:
counter = 0 while counter < 5: print(counter) counter += 1 # Output: # 0 # 1 # 2 # 3 # 4
In this example, the
while
loop continues to execute the code block as long as thecounter
variable is less than 5. The counter is incremented by 1 in each iteration, and when it reaches 5, the conditioncounter < 5
becomes false, and the loop stops.
Both for
and while
loops are essential tools for controlling the flow of your program and performing repetitive tasks. It's important to ensure that loops have a proper exit condition or termination point, as infinite loops can cause your program to hang or consume excessive resources.
Break and Continue Statements
Loop control statements are used to modify the execution flow of loops (for
and while
). They allow you to skip certain iterations, exit the loop prematurely, or perform a no-operation action. The three loop control statements in Python are break
, continue
, and pass
.
break
: Thebreak
statement is used to exit a loop (for
orwhile
) prematurely when a specific condition is met. Once thebreak
statement is executed, the loop is terminated, and the program continues with the next line after the loop.Example:
for i in range(10): if i == 5: break print(i) # Output: # 0 # 1 # 2 # 3 # 4
In this example, the
for
loop iterates over the numbers 0 to 9. Wheni
reaches 5, thebreak
statement is executed, and the loop is terminated, so numbers 5 to 9 are not printed.continue
: Thecontinue
statement is used to skip the remaining part of the loop's code block for the current iteration and proceed to the next iteration. This can be helpful when you want to avoid executing certain code for specific values in the loop.Example:
for i in range(10): if i % 2 == 0: continue print(i) # Output: # 1 # 3 # 5 # 7 # 9
In this example, the
for
loop iterates over the numbers 0 to 9. Ifi
is even (i.e.,i % 2 == 0
), thecontinue
statement is executed, and the loop proceeds to the next iteration without executing theprint(i)
statement. As a result, only odd numbers are printed.pass
: Thepass
statement is a no-operation (no-op) statement that does nothing when it's executed. It can be used as a placeholder when you need to define an empty code block, such as when you want to create a loop or a function that you plan to implement later.Example:
for i in range(10): if i % 2 == 0: pass # No action is taken for even numbers else: print(i) # Output: # 1 # 3 # 5 # 7 # 9
In this example, the
pass
statement is used as a placeholder for even numbers. The loop iterates over the numbers 0 to 9, and wheni
is even, thepass
statement is executed, doing nothing. Wheni
is odd, theprint(i)
statement is executed, printing the odd numbers.
In summary, loop control statements (break
, continue
, and pass
) are used to modify the flow of loops, allowing you to exit loops prematurely, skip specific iterations, or define placeholders for future implementation. They can be helpful in creating more efficient and flexible loops in your Python programs.
Strings and Characters
Comments and Doc Strings
In Python, comments and docstrings are used to provide human-readable explanations and documentation for your code. They help make your code more understandable and maintainable for both yourself and others who might read or work with your code in the future.
Comments: Comments in Python start with a
#
symbol and continue until the end of the line. Python ignores comments during execution, so they don't affect the behavior of your program.Example:
# This is a single-line comment print("Hello, World!") # This comment is at the end of the line ''' This is a multiline comment or block comment ''' print("Hello, again!")
In this example, there are three comments. The first two are single-line comments, and the third one is a multiline comment enclosed in triple single quotes (
'''
). Although triple quotes are typically used for docstrings, they can also be used for multiline comments when not used in the context of a function or class definition.Docstrings: Docstrings (short for "documentation strings") are special types of comments used to document functions, classes, and modules. They are enclosed in triple single (
'''
) or double ("""
) quotes and placed immediately after the function, class, or module definition.Example:
def add(a, b): ''' This function takes two numbers as input and returns their sum. Args: a (int or float): The first number b (int or float): The second number Returns: int or float: The sum of the two input numbers ''' return a + b print(add(2, 3))
In this example, a docstring is used to provide documentation for the
add
function. The docstring explains the purpose of the function, its input parameters (arguments), and the return value. This information can be helpful for users of the function and can also be used by tools like Sphinx or PyDoc to generate documentation automatically.
In summary, comments and docstrings are essential for providing explanations and documentation for your Python code. They improve the readability and maintainability of your code, making it easier for yourself and others to understand and work with your programs. Always consider adding meaningful comments and docstrings to your code to facilitate better collaboration and understanding.
Strings Methods
Python provides a variety of string methods that can be used to manipulate and process strings. Here are some of the most common string methods:
strip()
: Removes leading and trailing whitespace characters (spaces, tabs, and newlines) from a string.s = " Hello, World! " print(s.strip()) # Output: "Hello, World!"
lower()
andupper()
: Converts a string to lowercase or uppercase, respectively.s = "Hello, World!" print(s.lower()) # Output: "hello, world!" print(s.upper()) # Output: "HELLO, WORLD!"
split()
: Splits a string into a list of substrings based on a specified delimiter. If no delimiter is provided, it splits the string at whitespace characters.s = "Hello, World!" print(s.split()) # Output: ['Hello,', 'World!'] print(s.split(',')) # Output: ['Hello', ' World!']
join()
: Joins a list of strings into a single string using a specified delimiter.words = ['Hello', 'World'] delimiter = ' ' print(delimiter.join(words)) # Output: "Hello World"
replace()
: Replaces all occurrences of a substring with another substring.s = "Hello, World!" print(s.replace("World", "Python")) # Output: "Hello, Python!"
startswith()
andendswith()
: Checks if a string starts or ends with a specified substring, returningTrue
orFalse
.s = "Hello, World!" print(s.startswith("Hello")) # Output: True print(s.endswith("Python")) # Output: False
find()
andindex()
: Search for a substring in a string and return the index of the first occurrence. If the substring is not found,find()
returns -1, whileindex()
raises aValueError
.s = "Hello, World!" print(s.find("World")) # Output: 7 print(s.find("Python")) # Output: -1 print(s.index("World")) # Output: 7
count()
: Counts the number of non-overlapping occurrences of a substring in a string.s = "Hello, World! Welcome to the World of Python." print(s.count("World")) # Output: 2
format()
: Replaces placeholders in a string with specified values, allowing you to create formatted strings.template = "Hello, {name}! Welcome to {country}." print(template.format(name="Alice", country="Wonderland")) # Output: "Hello, Alice! Welcome to Wonderland."
These are some of the most commonly used string methods in Python. They provide a convenient way to manipulate and process strings, making it easier to work with text data in your programs. To explore more string methods, you can refer to the official Python documentation: https://docs.python.org/3/library/stdtypes.html#string-methods
Lists, Tuples and Dictionaries
Lists Methods
A list in Python is a mutable, ordered sequence of elements. Each element can be of any data type, including other lists. Lists are created by enclosing elements in square brackets ([]
).
Here are some common list methods in Python:
append()
: Adds an element to the end of the list.numbers = [1, 2, 3] numbers.append(4) print(numbers) # Output: [1, 2, 3, 4]
extend()
: Appends the elements of an iterable (e.g., list, tuple, or string) to the end of the list.numbers = [1, 2, 3] numbers.extend([4, 5, 6]) print(numbers) # Output: [1, 2, 3, 4, 5, 6]
insert()
: Inserts an element at a specified index in the list. All elements to the right of the inserted element are shifted to the right.numbers = [1, 2, 4] numbers.insert(2, 3) # Insert 3 at index 2 print(numbers) # Output: [1, 2, 3, 4]
remove()
: Removes the first occurrence of a specified element from the list. Raises aValueError
if the element is not found.numbers = [1, 2, 3, 2, 4] numbers.remove(2) print(numbers) # Output: [1, 3, 2, 4]
pop()
: Removes and returns the element at a specified index. If no index is provided, it removes and returns the last element of the list. Raises anIndexError
if the list is empty or the index is out of range.numbers = [1, 2, 3, 4] last_number = numbers.pop() print(last_number) # Output: 4 print(numbers) # Output: [1, 2, 3]
index()
: Returns the index of the first occurrence of a specified element in the list. Raises aValueError
if the element is not found. You can also provide optionalstart
andend
arguments to search within a subsequence of the list.numbers = [1, 2, 3, 2, 4] print(numbers.index(2)) # Output: 1
count()
: Returns the number of occurrences of a specified element in the list.numbers = [1, 2, 3, 2, 4] print(numbers.count(2)) # Output: 2
sort()
: Sorts the elements of the list in ascending order (or descending order if thereverse
parameter is set toTrue
). You can also provide a custom sorting function with thekey
parameter.numbers = [3, 1, 4, 2] numbers.sort() print(numbers) # Output: [1, 2, 3, 4]
reverse()
: Reverses the order of the elements in the list.numbers = [1, 2, 3, 4] numbers.reverse() print(numbers) # Output: [4, 3, 2, 1]
In this example, the
reverse()
method is called on thenumbers
list, which contains the elements[1, 2, 3, 4]
. The method reverses the order of the elements in the list, resulting in[4, 3, 2, 1]
, which is then printed out.copy()
: Returns a shallow copy of the list. This method can be used to create a new list with the same elements as the original list without modifying the original list.numbers = [1, 2, 3, 4] new_numbers = numbers.copy() print(new_numbers) # Output: [1, 2, 3, 4]
In this example, the
copy()
method is called on thenumbers
list, which contains the elements[1, 2, 3, 4]
. The method creates a new listnew_numbers
with the same elements as the originalnumbers
list, resulting in[1, 2, 3, 4]
, which is then printed out.clear()
: Removes all elements from the list, making it an empty list.numbers = [1, 2, 3, 4] numbers.clear() print(numbers) # Output: []
In this example, the
clear()
method is called on thenumbers
list, which contains the elements[1, 2, 3, 4]
. The method removes all elements from the list, making it an empty list[]
, which is then printed out.
Tuples Methods
A tuple in Python is an immutable, ordered sequence of elements. Similar to lists, elements in a tuple can be of any data type, including other tuples. Tuples are created by enclosing elements in parentheses (()
).
Since tuples are immutable, they have fewer methods compared to lists. Here are the two most common tuple methods:
count()
: Returns the number of occurrences of a specified element in the tuple.numbers = (1, 2, 3, 2, 4) print(numbers.count(2)) # Output: 2
In this example, the
count()
method is called on thenumbers
tuple, which contains the elements(1, 2, 3, 2, 4)
. The method returns the number of occurrences of the value2
, which is2
, and the result is printed out.index()
: Returns the index of the first occurrence of a specified element in the tuple. Raises aValueError
if the element is not found. You can also provide optionalstart
andend
arguments to search within a subsequence of the tuple.numbers = (1, 2, 3, 2, 4) print(numbers.index(2)) # Output: 1
In this example, the
index()
method is called on thenumbers
tuple, which contains the elements(1, 2, 3, 2, 4)
. The method returns the index of the first occurrence of the value2
, which is1
, and the result is printed out.
These are the two most common tuple methods in Python. While the tuple methods are limited compared to lists, tuples can still be used in many of the same ways as lists, such as indexing, slicing, and iterating through elements. The key difference is that tuples are immutable, which can be useful in situations where you want to create a collection of elements that should not be modified.
Dictionaries Methods
A dictionary in Python is an unordered, mutable collection of key-value pairs. Keys must be unique and hashable (e.g., strings, numbers, or tuples containing only hashable elements), while values can be of any data type, including other dictionaries. Dictionaries are created using curly braces ({}
) with key-value pairs separated by colons.
Here are some common dictionary methods in Python:
get()
: Returns the value associated with a specified key. If the key is not found, it returns a default value (orNone
if no default value is provided).person = {"name": "Alice", "age": 30} print(person.get("name")) # Output: "Alice" print(person.get("city", "NYC")) # Output: "NYC"
keys()
: Returns a view object displaying a list of all keys in the dictionary.person = {"name": "Alice", "age": 30} print(person.keys()) # Output: dict_keys(['name', 'age'])
values()
: Returns a view object displaying a list of all values in the dictionary.person = {"name": "Alice", "age": 30} print(person.values()) # Output: dict_values(['Alice', 30])
items()
: Returns a view object displaying a list of all key-value pairs (tuples) in the dictionary.person = {"name": "Alice", "age": 30} print(person.items()) # Output: dict_items([('name', 'Alice'), ('age', 30)])
update()
: Merges the contents of another dictionary or an iterable of key-value pairs into the current dictionary. If a key already exists, its value is updated with the new value.person = {"name": "Alice", "age": 30} person.update({"age": 31, "city": "NYC"}) print(person) # Output: {'name': 'Alice', 'age': 31, 'city': 'NYC'}
pop()
: Removes a specified key from the dictionary and returns its value. Raises aKeyError
if the key is not found (unless a default value is provided).person = {"name": "Alice", "age": 30} age = person.pop("age") print(age) # Output: 30 print(person) # Output: {'name': 'Alice'}
popitem()
: Removes and returns the last inserted key-value pair as a tuple. Raises aKeyError
if the dictionary is empty.person = {"name": "Alice", "age": 30} item = person.popitem() print(item) # Output: ('age', 30) print(person) # Output: {'name': 'Alice'}
clear()
: Removes all items from the dictionary, making it an empty dictionary.person = {"name": "Alice", "age": 30} person.clear() print(person) # Output: {}
These are some of the most commonly used dictionary methods in Python. They provide a convenient way to manipulate and process dictionaries, making it easier to work with key-value pairs in your programs. To explore more dictionary methods and functionalities, you can refer to the official Python documentation: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
Indexing, Slicing, Negative Indexing
Indexing, slicing, and negative indexing are techniques used to access or manipulate elements within ordered data structures such as strings, lists, and tuples in Python.
Indexing: Indexing allows you to access individual elements of an ordered data structure by their position (index) in the sequence. Indexing starts at 0 for the first element, 1 for the second element, and so on.
my_list = [10, 20, 30, 40, 50] print(my_list[1]) # Output: 20
Slicing: Slicing allows you to access a contiguous subsequence (slice) of elements in an ordered data structure. To create a slice, you need to provide a start index, an end index (exclusive), and an optional step value. The syntax is
sequence[start:end:step]
.my_list = [10, 20, 30, 40, 50] print(my_list[1:4]) # Output: [20, 30, 40] print(my_list[0:5:2]) # Output: [10, 30, 50]
Negative Indexing: Negative indexing allows you to access elements in an ordered data structure relative to the end of the sequence. The last element has an index of -1, the second to last element has an index of -2, and so on.
my_list = [10, 20, 30, 40, 50] print(my_list[-1]) # Output: 50 print(my_list[-3:]) # Output: [30, 40, 50]
These techniques make it easy to access and manipulate elements in ordered data structures in Python, simplifying the handling of sequences in your programs.
Functions
A function in Python is a named sequence of statements that performs a specific task or computation. Functions are defined using the def
keyword, followed by the function name and a pair of parentheses containing any input parameters. The function body is indented, and a return
statement is used to return the result of the computation (although it is optional).
Here's an example of defining and using a function that adds two numbers:
def add_numbers(a, b):
result = a + b
return result
# Calling the function
sum = add_numbers(3, 5)
print(sum) # Output: 8
In this example, a function called add_numbers
is defined, which takes two input parameters, a
and b
. The function adds the values of a
and b
, stores the result in the variable result
, and returns the value of result
.
The function can be called by providing the required arguments, in this case, the numbers 3 and 5. The returned value, 8, is then assigned to the variable sum
, which is printed out.
Functions help modularize your code, making it more organized, reusable, and easier to maintain. They allow you to break down complex tasks into smaller, more manageable pieces, which can improve the overall structure and readability of your programs.
Defining and Calling Functions
Defining functions: Defining a function means creating a named sequence of statements that performs a specific task. In Python, functions are defined using the def
keyword, followed by the function name, and a pair of parentheses containing any input parameters. The function body is indented.
def function_name(parameter1, parameter2):
# function body
# perform some task
return result
Calling functions: Calling a function means executing the function with specific input values (arguments) for its parameters. To call a function, use its name followed by the arguments within parentheses.
result = function_name(argument1, argument2)
Example:
# Defining a function to add two numbers
def add_numbers(a, b):
result = a + b
return result
# Calling the function with arguments 3 and 5
sum = add_numbers(3, 5)
print(sum) # Output: 8
In this example, we define a function called add_numbers
that takes two parameters, a
and b
, and returns their sum. We then call the function with the arguments 3 and 5, and the result, 8, is printed out.
Parameter, Arguments & Return
Parameters: Parameters are the input variables defined in a function's signature. They serve as placeholders for the actual values (arguments) that will be passed to the function when it is called. Parameters help specify what kind of input the function expects to receive and allow the function to work with different input values.
def function_name(parameter1, parameter2): # function body
Arguments: Arguments are the actual values that are passed to a function when it is called. They correspond to the parameters defined in the function's signature. When the function is executed, the parameters take on the values of the arguments supplied during the function call.
result = function_name(argument1, argument2)
There are two types of arguments in Python: positional arguments and keyword arguments. Positional arguments are matched to parameters based on their order, while keyword arguments are matched based on their names.
Return: The
return
statement in a function is used to send a value back to the caller after the function has completed its task. A function can return a single value, multiple values (as a tuple), or no value (implicitly returningNone
). Thereturn
statement is optional; if it is not present, the function will returnNone
by default.def function_name(parameter1, parameter2): # function body return result
Example:
# Defining a function with parameters 'a' and 'b'
def add_numbers(a, b):
result = a + b
return result
# Calling the function with arguments 3 and 5
sum = add_numbers(3, 5) # The values 3 and 5 are passed as arguments
print(sum) # Output: 8
In this example, the add_numbers
function has two parameters, a
and b
. When the function is called with the arguments 3 and 5, the parameters take on these values, and the function calculates their sum. The return
statement sends the result (8) back to the caller, and it is printed out.
Formal & Actual Arguments (arg, arg, *karg)
In Python, there are different ways to pass arguments to a function. These include formal arguments (parameters) and actual arguments (arguments). Formal arguments are defined in the function signature, while actual arguments are the values passed to the function when it is called. There are three types of formal arguments: positional arguments, *args
, and **kwargs
.
Positional arguments: Positional arguments are the most common type of formal arguments. They are matched to the actual arguments based on their position in the function definition.
def function_name(arg1, arg2): # function body
When calling the function, the actual arguments are provided in the same order as the formal arguments in the function definition.
function_name(value1, value2)
*args
: The*args
syntax allows a function to accept a variable number of positional arguments. When defining the function, you can use an asterisk (*) before the parameter name to indicate that it can accept a variable number of arguments. Inside the function,args
is treated as a tuple containing all the provided positional arguments.def function_name(arg1, arg2, *args): # function body
When calling the function, you can pass any number of positional arguments after the required arguments.
function_name(value1, value2, value3, value4, value5)
**kwargs
: The**kwargs
syntax allows a function to accept a variable number of keyword arguments. When defining the function, you can use two asterisks (**) before the parameter name to indicate that it can accept a variable number of keyword arguments. Inside the function,kwargs
is treated as a dictionary containing all the provided keyword arguments.def function_name(arg1, arg2, **kwargs): # function body
When calling the function, you can pass any number of keyword arguments after the required arguments.
function_name(value1, value2, key1=value3, key2=value4)
Here's an example using all three types of formal arguments:
def sample_function(a, b, *args, **kwargs):
print("a:", a)
print("b:", b)
print("args:", args)
print("kwargs:", kwargs)
sample_function(1, 2, 3, 4, 5, key1="value1", key2="value2")
Output:
a: 1
b: 2
args: (3, 4, 5)
kwargs: {'key1': 'value1', 'key2': 'value2'}
In this example, the sample_function
has positional arguments a
and b
, as well as *args
and **kwargs
to accept a variable number of additional positional and keyword arguments.
Local & Global Scope
In Python, variables have different scopes depending on where they are defined. The two main scopes are local and global scope.
Local scope: Variables defined inside a function have a local scope, meaning they can only be accessed within that function. When the function finishes executing, the local variables are destroyed, and their values cannot be accessed outside the function.
Global scope: Variables defined outside any function have a global scope, meaning they can be accessed from anywhere in the code, including inside functions (unless a local variable with the same name exists within the function).
Here's an example illustrating the difference between local and global variables:
# Global variable
global_var = "I'm a global variable"
def my_function():
# Local variable
local_var = "I'm a local variable"
# Accessing the global variable within the function
print("Inside the function:")
print(global_var)
print(local_var)
# Calling the function
my_function()
# Accessing the variables outside the function
print("\nOutside the function:")
print(global_var)
# This will result in an error since the local variable is not accessible outside the function
print(local_var)
Output:
Inside the function:
I'm a global variable
I'm a local variable
Outside the function:
I'm a global variable
As you can see, the global variable global_var
can be accessed both inside and outside the function, while the local variable local_var
can only be accessed within the function. Attempting to access the local variable outside the function results in an error.
If you need to modify a global variable inside a function, you can use the global
keyword before the variable name:
global_counter = 0
def increment_counter():
global global_counter
global_counter += 1
print("Counter:", global_counter)
increment_counter()
increment_counter()
Output:
Counter: 1
Counter: 2
In this example, we use the global
keyword inside the increment_counter
function to tell Python that we want to modify the global variable global_counter
instead of creating a new local variable with the same name.
Object Oriented Programming (OOP)
Object-Oriented Programming (OOP) is a programming paradigm that revolves around the concept of objects, which are instances of classes. The main idea behind OOP is to combine data and functions that operate on the data into a single unit called a class. An object is a specific instance of a class, containing its own set of data and functions, which are called methods.
The primary goals of OOP are to improve code organization, reusability, and modularity, making it easier to design, maintain, and scale complex software systems. OOP is based on several key principles, including:
Encapsulation: Encapsulation is the process of bundling data (attributes) and the methods that operate on that data within a single unit (class). This helps to hide the internal workings of a class from the outside world and restrict access to its internal state, ensuring that the object's state is changed only through its methods.
Inheritance: Inheritance is a way to create a new class by deriving it from an existing class, thereby reusing and extending the functionality of the existing class. The new class is called the subclass (or derived class), and the existing class is the superclass (or base class). Inheritance enables you to create hierarchical relationships between classes, promoting reusability and modularity.
Polymorphism: Polymorphism refers to the ability of a function or method to take on different forms based on the object it is called on or the arguments it receives. In OOP, polymorphism allows a single interface (e.g., a function or method signature) to represent different types of operations on different classes or objects. This enables you to write more flexible and reusable code that can work with various types of objects without knowing their specific implementation details.
Abstraction: Abstraction is the process of simplifying complex systems by breaking them down into smaller, more manageable parts, focusing on the essential features and hiding the complexities. In OOP, abstraction is achieved through the use of classes and interfaces, which define the essential characteristics and behaviors of an object without revealing its internal implementation details.
In Python, OOP is supported through the use of classes, which can be created using the class
keyword. Python classes can have attributes (data members) and methods (member functions) that define the properties and behaviors of the objects created from the class. Here's an example of a simple class in Python:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
print("Woof! Woof!")
# Create an object (instance) of the Dog class
my_dog = Dog("Buddy", "Golden Retriever")
# Access the object's attributes
print(my_dog.name) # Output: Buddy
print(my_dog.breed) # Output: Golden Retriever
# Call the object's method
my_dog.bark() # Output: Woof! Woof!
In this example, we define a Dog
class with an __init__
method (constructor) and a bark
method. We then create an object of the Dog
class, set its attributes, and call its method.
Classes and Objects
In Python, classes and objects are fundamental concepts in object-oriented programming. A class is a blueprint for creating objects, while an object is an instance of a class.
Classes:
A class is a code template for creating objects. It defines the structure and behavior of objects through attributes (data members) and methods (member functions). You can think of a class as a blueprint or prototype from which objects are created.
To define a class in Python, you use the class
keyword, followed by the class name and a colon. The class body is then indented, similar to functions and other code blocks.
Here's a simple example of a class definition:
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
print("Woof! Woof!")
In this example, we define a Dog
class with an __init__
method (constructor) and a bark
method.
Objects:
An object is an instance of a class. It has its own state (attributes) and behavior (methods). When a class is defined, only the description of the object is created, not the object itself. To create an object, you need to instantiate the class, which involves calling the class as if it were a function.
Here's how to create an object of the Dog
class:
my_dog = Dog("Buddy", "Golden Retriever")
In this example, we create a new object my_dog
of the Dog
class and pass the required arguments to the __init__
method (the constructor). Now, my_dog
is an instance of the Dog
class with its own attributes and methods.
You can access the object's attributes and methods using the dot (.) notation:
# Access the object's attributes
print(my_dog.name) # Output: Buddy
print(my_dog.breed) # Output: Golden Retriever
# Call the object's method
my_dog.bark() # Output: Woof! Woof!
In this example, we access the name
and breed
attributes of the my_dog
object and call its bark
method.
__init__() Method and 'self ' Parameter
In Python, __init__
and self
are used in the context of object-oriented programming when working with classes and objects. Here's a brief explanation of both terms along with an example:
__init__
:__init__
is a special method in Python classes, often referred to as the constructor. It is called automatically when a new object is created from a class. This method is typically used to initialize the object's attributes with default or user-provided values.Example:
class Dog: def __init__(self, name, breed): self.name = name self.breed = breed
In this example, the
__init__
method takes two parameters,name
andbreed
, in addition to theself
parameter. When a new object is created from theDog
class, the__init__
method is called, and the object'sname
andbreed
attributes are initialized with the provided values.self
:self
is a reference to the instance (object) of the class. It is used within class methods to access the object's attributes and other methods. Theself
parameter is not a keyword; it is a convention used in Python to represent the instance of the class. You can name it differently if you prefer, but it is highly recommended to stick to theself
convention for clarity and consistency.Example:
class Dog: def __init__(self, name, breed): self.name = name self.breed = breed def bark(self): print(f"{self.name} says Woof! Woof!")
In this example, the
self
parameter is used within the__init__
method to set the object'sname
andbreed
attributes. In thebark
method,self
is used to access the object'sname
attribute.
Here's how to create a new Dog
object and call its bark
method:
my_dog = Dog("Buddy", "Golden Retriever")
my_dog.bark() # Output: Buddy says Woof! Woof!
When calling the bark
method, you don't need to pass the self
parameter explicitly. Python automatically provides the reference to the instance (the my_dog
object, in this case) when you call a method on an object.
Multiple Constructors
In Python, you cannot define multiple constructors with different numbers of arguments like you can in some other languages. However, you can achieve similar functionality using default argument values and class methods.
Here's an example that demonstrates how to define a class with multiple constructor-like behaviors:
class Dog:
def __init__(self, name=None, breed=None):
self.name = name
self.breed = breed
@classmethod
def with_name(cls, name):
return cls(name, breed=None)
@classmethod
def with_breed(cls, breed):
return cls(name=None, breed=breed)
def __str__(self):
return f"Dog(name={self.name}, breed={self.breed})"
In this example:
We define the
__init__
method with default values forname
andbreed
arguments. This allows creating aDog
object without providing any arguments, with only a name, with only a breed, or with both.We define two class methods,
with_name
andwith_breed
, that create new instances of theDog
class with only the specified attribute set. These methods serve as alternative constructors.
Here's how you can create Dog
objects using these methods:
# Using the default constructor
dog1 = Dog()
print(dog1) # Output: Dog(name=None, breed=None)
# Using the default constructor with a name and breed
dog2 = Dog("Buddy", "Golden Retriever")
print(dog2) # Output: Dog(name=Buddy, breed=Golden Retriever)
# Using the with_name class method
dog3 = Dog.with_name("Max")
print(dog3) # Output: Dog(name=Max, breed=None)
# Using the with_breed class method
dog4 = Dog.with_breed("Labrador")
print(dog4) # Output: Dog(name=None, breed=Labrador)
By using default argument values and class methods, you can effectively create multiple constructor-like behaviors in a Python class.
Encapsulation
Encapsulation is one of the fundamental principles of object-oriented programming (OOP). It refers to the practice of bundling data (attributes) and the methods that operate on that data within a single unit, which is a class. Encapsulation allows you to hide the internal implementation details of a class and control access to its attributes, ensuring that the object's state is changed only through its methods.
Here's an example that demonstrates encapsulation in Python:
class BankAccount:
def __init__(self, account_number, balance=0):
self._account_number = account_number
self._balance = balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"Deposited ${amount}. New balance: ${self._balance}")
else:
print("Invalid deposit amount.")
def withdraw(self, amount):
if amount > 0 and amount <= self._balance:
self._balance -= amount
print(f"Withdrew ${amount}. New balance: ${self._balance}")
else:
print("Invalid withdrawal amount or insufficient balance.")
def get_balance(self):
return self._balance
def get_account_number(self):
return self._account_number
In this example, we define a BankAccount
class that encapsulates the account number and balance attributes, as well as the methods for depositing and withdrawing money. We use the underscore prefix _
to indicate that these attributes should be considered private (not accessible directly from outside the class). Although Python does not enforce strict access control like some other programming languages, the single underscore prefix is a convention that signals to users of the class that these attributes should not be accessed directly.
Instead of accessing the _balance
attribute directly, we provide methods like deposit
, withdraw
, and get_balance
to interact with the account's balance. This ensures that the account's balance can only be changed through the provided methods, which can perform necessary validation and maintain the integrity of the account data.
Here's how you can use the BankAccount
class:
account = BankAccount("12345678")
# Deposit money
account.deposit(100) # Output: Deposited $100. New balance: $100
# Withdraw money
account.withdraw(50) # Output: Withdrew $50. New balance: $50
# Get the balance
print(f"Current balance: ${account.get_balance()}") # Output: Current balance: $50
# Get the account number
print(f"Account number: {account.get_account_number()}") # Output: Account number: 12345678
In this example, encapsulation helps maintain the integrity of the BankAccount
class and control access to its attributes, ensuring that the account balance can only be changed through the provided methods.
Public And Private Methods
In object-oriented programming, public and private methods are concepts related to access control and encapsulation. They determine the visibility and accessibility of class methods outside the class.
Public methods: Public methods are methods that can be accessed from anywhere, including from outside the class. They are part of the class's public interface and are used by other objects or code to interact with the class. In Python, all methods are public by default.
Example:
class Circle: def __init__(self, radius): self.radius = radius def area(self): return 3.14159 * self.radius ** 2
In this example, the
area
method is a public method, as it can be called from outside theCircle
class.Private methods: Private methods are methods that are meant to be used only within the class and should not be accessed from outside the class. They are used for internal operations and implementation details that should be hidden from other objects or code. In Python, there is no strict enforcement of private methods, but you can indicate that a method should be treated as private by using a double underscore prefix
__
before the method name. This causes Python to "mangle" the method name, making it harder (but not impossible) to access from outside the class.Example:
class Circle: def __init__(self, radius): self.radius = radius def area(self): return self.__calculate_area() def __calculate_area(self): return 3.14159 * self.radius ** 2
In this example, the
__calculate_area
method is a private method, as indicated by the double underscore prefix. It is intended to be used only within theCircle
class and should not be accessed from outside the class.
Remember that Python does not enforce strict access control like some other programming languages. The single and double underscore prefixes are conventions that signal the intended use of attributes and methods, but they can still be accessed from outside the class if you know how to work around the name mangling. However, it is generally a good practice to respect the intended access control and follow the conventions when working with classes and objects.
Inheritance
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows one class to inherit attributes and methods from another class. Inheritance enables code reusability and the creation of hierarchical relationships between classes. In Python, you can create a new class that inherits from an existing class by specifying the base (parent) class in parentheses after the new class's name.
Here's an example that demonstrates inheritance in Python:
# Base (parent) class: Animal
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def speak(self):
print("The animal makes a sound")
# Derived (child) class: Dog, inheriting from Animal
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age)
self.breed = breed
def speak(self):
print("The dog barks")
# Derived (child) class: Cat, inheriting from Animal
class Cat(Animal):
def speak(self):
print("The cat meows")
In this example:
We define a base (parent) class called
Animal
with a constructor (__init__
) and aspeak
method.We create two derived (child) classes,
Dog
andCat
, that inherit from theAnimal
class.The
Dog
class adds a new attribute,breed
, and overrides thespeak
method to provide its own implementation.The
Cat
class overrides thespeak
method to provide its own implementation.The
super().__init__(name, age)
call in theDog
class's constructor is used to call the parent class's constructor, ensuring the proper initialization of thename
andage
attributes.
Here's how you can use these classes:
animal = Animal("Generic Animal", 5)
animal.speak() # Output: The animal makes a sound
dog = Dog("Buddy", 3, "Golden Retriever")
print(dog.name, dog.age, dog.breed) # Output: Buddy 3 Golden Retriever
dog.speak() # Output: The dog barks
cat = Cat("Whiskers", 2)
print(cat.name, cat.age) # Output: Whiskers 2
cat.speak() # Output: The cat meows
In this example, inheritance allows the Dog
and Cat
classes to reuse the code from the Animal
class and override or extend it as needed, resulting in a more organized and maintainable code structure.
Getter and Setter
In object-oriented programming, getters and setters are methods used to control access to an object's attributes. They provide a way to retrieve (get) and modify (set) the values of an attribute while maintaining the encapsulation principle. Python provides the property
decorator and the @<attribute>.setter
decorator to create getters and setters in a Pythonic way.
Here's an example that demonstrates how to use getters and setters in Python:
class Employee:
def __init__(self, name, salary):
self._name = name
self._salary = salary
# Getter for 'name' attribute
@property
def name(self):
return self._name
# Setter for 'name' attribute
@name.setter
def name(self, value):
if not value:
raise ValueError("Name cannot be empty")
self._name = value
# Getter for 'salary' attribute
@property
def salary(self):
return self._salary
# Setter for 'salary' attribute
@salary.setter
def salary(self, value):
if value < 0:
raise ValueError("Salary cannot be negative")
self._salary = value
In this example:
We define an
Employee
class with two private attributes,_name
and_salary
.We use the
@property
decorator to create a getter method for the_name
attribute. The method name matches the attribute name, but without the underscore prefix (e.g.,name
).We use the
@name.setter
decorator to create a setter method for the_name
attribute. The method takes one argument (besidesself
), which is the new value for the attribute. The method checks if the new value is valid and sets the attribute accordingly.We follow the same approach to create getter and setter methods for the
_salary
attribute.
Here's how you can use the Employee
class and its getters and setters:
employee = Employee("John Doe", 50000)
# Get the name and salary using the getter methods
print(employee.name) # Output: John Doe
print(employee.salary) # Output: 50000
# Set the name and salary using the setter methods
employee.name = "Jane Doe"
employee.salary = 55000
# Get the updated name and salary
print(employee.name) # Output: Jane Doe
print(employee.salary) # Output: 55000
# Attempt to set an invalid name or salary (raises ValueError)
employee.name = ""
employee.salary = -100
In this example, getters and setters provide a controlled way to access and modify the _name
and _salary
attributes, ensuring that the encapsulation principle is maintained and allowing for validation of the new values before they are set.
Creating And Importing Module
A module in Python is a file containing Python code, such as functions, classes, or variables, that can be imported and used by other Python scripts. Creating a module helps you organize your code, making it more maintainable and reusable.
Here's a step-by-step guide on creating and importing a module in Python:
Create a new Python file (module) with the
.py
extension. For example, let's create a file namedmymodule.py
.Write your functions, classes, or variables inside the
mymodule.py
file. For example, let's add a simple function and a variable:
# mymodule.py
def greet(name):
return f"Hello, {name}!"
welcome_message = "Welcome to my module!"
Save the
mymodule.py
file in a directory that's part of your Python project.In another Python script (e.g.,
main.py
), you can now import the functions, classes, or variables from themymodule.py
file using theimport
statement. For example:
# main.py
# Import the entire module
import mymodule
# Use the greet() function and welcome_message variable from mymodule
print(mymodule.greet("John"))
print(mymodule.welcome_message)
- Alternatively, you can import specific functions, classes, or variables from the module using the
from ... import ...
statement. For example:
# main.py
# Import specific items from the module
from mymodule import greet, welcome_message
# Use the greet() function and welcome_message variable directly
print(greet("Jane"))
print(welcome_message)
- Run your
main.py
script, and it will use the functions, classes, or variables from themymodule.py
file.
Note that if your module is located in a different directory, you may need to add the module's directory to your Python project's search path using the sys.path.append()
method:
import sys
sys.path.append('/path/to/your/module/directory')
This will ensure that Python can find and import your module properly.
Creating User-Defined Module
Creating a user-defined module in Python is a simple process. It involves creating a Python file with a .py
extension and writing your functions, classes, or variables inside that file. Then, you can import this module into other Python scripts to use the defined functions, classes, or variables.
Here's an example of creating a user-defined module and using it in another script:
Create a new Python file named
my_module.py
in your project directory.Write your functions, classes, or variables inside the
my_module.py
file. For example:
# my_module.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
PI = 3.14159
Save the
my_module.py
file.Now, create another Python script in the same directory (e.g.,
main.py
). You can import the user-defined module using theimport
statement:
# main.py
import my_module
result1 = my_module.add(10, 20)
result2 = my_module.subtract(30, 15)
area = my_module.PI * (5 ** 2)
print(f"Result of addition: {result1}")
print(f"Result of subtraction: {result2}")
print(f"Area of a circle with radius 5: {area}")
- Run the
main.py
script, and it will use the functions and variables from themy_module.py
file.
By creating a user-defined module, you can organize your code better and make it more maintainable and reusable. Other scripts in your project can import and use the functions, classes, or variables defined in your module.
Multiple Inheritance
Multiple inheritance is a feature in object-oriented programming where a class can inherit attributes and methods from more than one parent class. This allows a class to combine the functionalities of multiple parent classes. Python supports multiple inheritance, and you can create a class that inherits from multiple parent classes by specifying them in parentheses, separated by commas, after the class's name.
Here's an example that demonstrates multiple inheritance in Python:
# Parent class 1: Swimmer
class Swimmer:
def swim(self):
print("The animal swims")
# Parent class 2: Walker
class Walker:
def walk(self):
print("The animal walks")
# Parent class 3: Flyer
class Flyer:
def fly(self):
print("The animal flies")
# Derived (child) class: Dolphin, inheriting from Swimmer and Walker
class Dolphin(Swimmer, Walker):
pass
# Derived (child) class: Duck, inheriting from Swimmer, Walker, and Flyer
class Duck(Swimmer, Walker, Flyer):
pass
In this example:
We define three parent classes:
Swimmer
,Walker
, andFlyer
, each with their own unique method.We create a derived (child) class
Dolphin
that inherits from bothSwimmer
andWalker
parent classes.We create another derived (child) class
Duck
that inherits from all three parent classes:Swimmer
,Walker
, andFlyer
.
Here's how you can use these classes:
dolphin = Dolphin()
dolphin.swim() # Output: The animal swims
dolphin.walk() # Output: The animal walks
duck = Duck()
duck.swim() # Output: The animal swims
duck.walk() # Output: The animal walks
duck.fly() # Output: The animal flies
In this example, multiple inheritance allows the Dolphin
and Duck
classes to inherit functionalities from multiple parent classes, combining their methods as needed.
Keep in mind that multiple inheritance can sometimes lead to complications, such as the diamond problem, where a class inherits from two classes that have a common parent, causing ambiguity in the method resolution order. Python resolves this issue using the C3 linearization (or Method Resolution Order - MRO) algorithm, which determines a consistent order in which the base classes are searched when looking for a method. You can inspect the MRO for a class using the __mro__
attribute or the mro()
method.
super() Function
In Python, super()
is a built-in function that is used to call a method from the parent (super) class. This function is often used in the constructor (__init__
) of a child class to ensure that the initialization code of the parent class is executed before the child class's own initialization code. super()
can also be used to call other methods from the parent class that have been overridden in the child class.
The super()
function returns a temporary object of the parent class, allowing you to call its methods without explicitly specifying the parent class's name.
Here's an example that demonstrates how to use super()
:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def display_info(self):
print(f"Name: {self.name}, Age: {self.age}")
class Employee(Person):
def __init__(self, name, age, company):
# Call the __init__ method of the parent (Person) class
super().__init__(name, age)
self.company = company
def display_info(self):
# Call the display_info method of the parent (Person) class
super().display_info()
print(f"Company: {self.company}")
# Create an Employee object
employee = Employee("John Doe", 30, "Acme Corp")
# Call the display_info method of the Employee class
employee.display_info()
In this example:
We define a
Person
class with a constructor (__init__
) and adisplay_info
method.We create a child class
Employee
that inherits from thePerson
class.In the
Employee
class's constructor, we usesuper().__init__(name, age)
to call the parent class's constructor. This ensures that thename
andage
attributes are properly initialized by the parent class's constructor before thecompany
attribute is set in the child class's constructor.We override the
display_info
method in theEmployee
class. Inside this method, we usesuper().display_info()
to call the parent class'sdisplay_info
method. This allows us to display the person's name and age before displaying the employee's company.
When we run the script and create an Employee
object, the output will be:
Name: John Doe, Age: 30
Company: Acme Corp
In this example, super()
helps us to call the parent class's methods in a more flexible and maintainable way, without explicitly specifying the parent class's name. This makes it easier to change the parent class or method names if needed, without having to update the child class code.
Composition
Composition is a design principle in object-oriented programming where a class is composed of one or more objects of other classes. It represents a "has-a" relationship between classes, meaning that one class has an instance of another class as an attribute. This allows you to create more complex objects by combining simpler objects and leveraging their functionality.
Composition is used to model real-world scenarios where an object is made up of other objects, and it promotes code reusability, maintainability, and modularity.
Here's an example to demonstrate composition in Python:
class Engine:
def __init__(self, type):
self.type = type
def start(self):
print(f"The {self.type} engine starts.")
class Car:
def __init__(self, make, model, engine_type):
self.make = make
self.model = model
self.engine = Engine(engine_type)
def start_engine(self):
self.engine.start()
def display_info(self):
print(f"Car Make: {self.make}, Model: {self.model}, Engine: {self.engine.type}")
# Create a Car object with an Engine object as an attribute
my_car = Car("Toyota", "Corolla", "gasoline")
# Call the start_engine() method of the Car object, which in turn calls the start() method of the Engine object
my_car.start_engine()
# Display the car's information
my_car.display_info()
In this example:
We define an
Engine
class with a constructor (__init__
) and astart
method.We create a
Car
class with a constructor (__init__
) that takes the car's make, model, and engine type as arguments.Inside the
Car
class's constructor, we create an instance of theEngine
class using the provided engine type and assign it to theengine
attribute of theCar
class.We define a
start_engine
method in theCar
class that calls thestart
method of theEngine
object.We create a
Car
object, which has anEngine
object as an attribute.
When we run the script, the output will be:
The gasoline engine starts.
Car Make: Toyota, Model: Corolla, Engine: gasoline
In this example, the Car
class is composed of an Engine
object, demonstrating the "has-a" relationship between the two classes. By using composition, we can easily create more complex objects by combining simpler objects and their functionality.
Aggregation
Aggregation is a design principle in object-oriented programming that represents a "has-a" relationship between classes, similar to composition. However, aggregation differs from composition in that the lifetime of the aggregated objects is independent of the lifetime of the aggregating object. In other words, when an object containing other objects (the aggregating object) is destroyed, the contained objects can continue to exist and be used elsewhere.
Here's an example to demonstrate aggregation in Python:
class Battery:
def __init__(self, capacity):
self.capacity = capacity
def display_capacity(self):
print(f"Battery capacity: {self.capacity} mAh")
class Smartphone:
def __init__(self, brand, model, battery):
self.brand = brand
self.model = model
self.battery = battery
def display_info(self):
print(f"Smartphone Brand: {self.brand}, Model: {self.model}")
self.battery.display_capacity()
# Create a Battery object
battery = Battery(4000)
# Create a Smartphone object with the Battery object as an attribute
my_smartphone = Smartphone("Samsung", "Galaxy S22", battery)
# Display the smartphone's information, including battery capacity
my_smartphone.display_info()
In this example:
We define a
Battery
class with a constructor (__init__
) and adisplay_capacity
method.We create a
Smartphone
class with a constructor (__init__
) that takes the smartphone's brand, model, and aBattery
object as arguments.Inside the
Smartphone
class's constructor, we assign the providedBattery
object to thebattery
attribute of theSmartphone
class.We define a
display_info
method in theSmartphone
class that displays the smartphone's brand, model, and battery capacity.We create a
Battery
object and aSmartphone
object that takes theBattery
object as an attribute.
When we run the script, the output will be:
Smartphone Brand: Samsung, Model: Galaxy S22
Battery capacity: 4000 mAh
In this example, the Smartphone
class has a "has-a" relationship with the Battery
class through aggregation. The Smartphone
object takes an existing Battery
object as an attribute, and the lifetime of the Battery
object is independent of the Smartphone
object. The Battery
object can be used in other instances, even if the Smartphone
object is destroyed.
Abstract Classes
In object-oriented programming, an abstract class is a class that cannot be instantiated directly. It serves as a blueprint for other classes that inherit from it. Abstract classes can have abstract methods, which are methods without a defined implementation in the abstract class. Subclasses of an abstract class are required to provide an implementation for these abstract methods.
In Python, you can define abstract classes using the abc
module (Abstract Base Class). Here's an example of defining and using an abstract class in Python:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# The following line would raise a TypeError since Animal is an abstract class
# animal = Animal()
# We can create instances of Dog and Cat classes that inherit from Animal
dog = Dog()
cat = Cat()
print(dog.speak()) # Output: Woof!
print(cat.speak()) # Output: Meow!
In this example:
We import
ABC
andabstractmethod
from theabc
module.We define an
Animal
class that inherits fromABC
. This indicates thatAnimal
is an abstract class.We define an abstract method
speak()
using the@abstractmethod
decorator inside theAnimal
class. This method has no implementation in the abstract class and is marked by thepass
statement.We create two subclasses
Dog
andCat
that inherit from theAnimal
abstract class.We provide an implementation for the
speak()
method in both theDog
andCat
classes.We cannot create an instance of the
Animal
class directly, as it's an abstract class. If we try to do so, it will raise aTypeError
.We can create instances of the
Dog
andCat
classes, which inherit from theAnimal
abstract class, and call theirspeak()
methods.
Abstract classes are useful when you want to establish a common interface or behavior for a group of related classes, without providing a concrete implementation in the base class. It ensures that subclasses implement the required methods, promoting consistency and reducing potential errors in the code.
import & from
In Python, the import
statement is used to load and access modules, which are collections of functions, classes, and variables defined in separate files. The from
statement is an additional way to import specific functions, classes, or variables from a module.
Here's an explanation of how import
and from
work, along with examples:
Using
import
to load a module: Theimport
statement is followed by the name of the module you want to import. Once a module is imported, you can access its functions, classes, and variables using the dot notation (module_name.function_name
).Example:
import math # Use the math module's sqrt function result = math.sqrt(25) print(result) # Output: 5.0
In this example, we import the built-in
math
module and use itssqrt
function to calculate the square root of 25.Using
from
to import specific functions, classes, or variables: Thefrom
statement is followed by the module name and theimport
keyword. You can then specify the functions, classes, or variables you want to import from the module, separated by commas. This allows you to access the imported components directly without using the dot notation.Example:
from math import sqrt, pi # Use the imported sqrt function directly result = sqrt(25) print(result) # Output: 5.0 # Use the imported pi constant directly print(pi) # Output: 3.141592653589793
In this example, we use the
from
statement to import thesqrt
function and thepi
constant from themath
module. We can then use them directly in our code without the dot notation.Using
from
with theas
keyword: Sometimes, you might want to import a module or a specific component from a module with a different name to avoid naming conflicts or to make the code more readable. You can use theas
keyword to specify an alias for the module or component.Example:
import math as m # Use the aliased math module's sqrt function result = m.sqrt(25) print(result) # Output: 5.0 from math import pi as PI # Use the aliased pi constant print(PI) # Output: 3.141592653589793
In this example, we import the
math
module with the aliasm
and import thepi
constant from themath
module with the aliasPI
. We can then use these aliases in our code.
The import
and from
statements provide different ways to load and access modules and their components in Python. By understanding and using these statements, you can leverage the functionality provided by Python modules and make your code more organized and efficient.
Operator Overloading
Operator overloading in Python allows you to define custom behavior for built-in operators (such as +
, -
, *
, /
, etc.) when they are used with objects of your custom class. This can make your code more intuitive and easier to read.
To overload an operator, you need to define a special method in your class. These special methods have double underscores (__
) at the beginning and end of their names and are also called "dunder" methods.
Here's an example of operator overloading in Python:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
raise TypeError("Can only add Vector objects")
def __sub__(self, other):
if isinstance(other, Vector):
return Vector(self.x - other.x, self.y - other.y)
raise TypeError("Can only subtract Vector objects")
vector1 = Vector(3, 4)
vector2 = Vector(1, 2)
vector3 = vector1 + vector2 # Uses the __add__ method
vector4 = vector1 - vector2 # Uses the __sub__ method
print(vector3) # Output: Vector(4, 6)
print(vector4) # Output: Vector(2, 2)
In this example:
We define a
Vector
class with a constructor (__init__
) that takes the x and y coordinates as arguments.We define a
__repr__
method for a human-readable representation of theVector
object when printed.We define a
__add__
method that overloads the+
operator. It takes anotherVector
object as an argument, checks if the argument is an instance ofVector
, and returns a newVector
object with the sum of the x and y coordinates of both vectors. If the argument is not aVector
object, it raises aTypeError
.We define a
__sub__
method that overloads the-
operator. It takes anotherVector
object as an argument, checks if the argument is an instance ofVector
, and returns a newVector
object with the difference between the x and y coordinates of both vectors. If the argument is not aVector
object, it raises aTypeError
.We create two
Vector
objects,vector1
andvector2
.We perform addition and subtraction operations using the overloaded
+
and-
operators, which internally call the__add__
and__sub__
methods.We print the resulting
Vector
objects,vector3
andvector4
.
By overloading operators, we can make our custom classes behave more intuitively, allowing for more readable and expressive code.
Naming Convention
In Python, there are widely-accepted naming conventions for various components in object-oriented programming, such as classes, methods, functions, and variables. Following these conventions makes your code more consistent, readable, and understandable to others.
Here's a summary of the naming conventions for each component:
Classes: Class names should be in PascalCase, which means that the first letter of each word in the name should be capitalized without underscores between words. This makes it easy to identify class names in your code.
Example:
class MyClass: pass
Methods and Functions: Method and function names should be in snake_case, which means that all words in the name should be lowercase with underscores between words. This makes method and function names easy to read and distinguish from class names.
Example:
def my_function(): pass class MyClass: def my_method(self): pass
Variables: Variable names should also be in snake_case, following the same conventions as method and function names. This makes variable names consistent and easy to read.
Example:
my_variable = 42 class MyClass: def my_method(self): my_local_variable = 10
Constants: Constants are variables whose values do not change during the program's execution. They should be written in uppercase snake_case, with underscores between words. This makes it clear that these variables are constants and should not be modified.
Example:
MY_CONSTANT = 3.14159
Private attributes and methods: For private attributes and methods in a class, you should use a single leading underscore before the name. This is a convention that signals that these components are intended for internal use within the class and should not be accessed directly from outside the class.
Example:
class MyClass: def __init__(self): self._my_private_attribute = 10 def _my_private_method(self): pass
Name mangling: If you want to make an attribute or method less visible to subclasses or harder to accidentally override, you can use name mangling by adding two leading underscores before the name. Python will automatically prepend the class name with an underscore to the attribute or method name, making it less likely to cause conflicts.
Example:
class MyClass: def __init__(self): self.__my_mangled_attribute = 10 def __my_mangled_method(self): pass
By following these naming conventions in your Python code, you can make your code more readable, maintainable, and consistent with the conventions followed by the Python community.
Errors And Exceptions Handling
Error and exception handling is an essential part of programming, as it allows you to gracefully handle unexpected situations that may occur during your program's execution. In Python, error handling is done using the try
, except
, else
, and finally
keywords.
Errors and Exceptions
In Python, errors are classified into two main categories: syntax errors and exceptions. Syntax errors occur when the code is not properly formed and cannot be executed, while exceptions are events that are triggered during the execution of the code when an error occurs.
try
andexcept
The basic structure for exception handling in Python is the
try
andexcept
block. The code that might raise an exception is placed inside thetry
block, and the code that should execute when an exception occurs is placed inside theexcept
block.Example:
try: x = 1 / 0 except ZeroDivisionError: print("You can't divide by zero!")
In this example, if the code inside the
try
block raises aZeroDivisionError
, the code inside theexcept
block will be executed, and the program will continue running without crashing.Handling multiple exceptions
You can handle multiple exceptions by specifying multiple
except
blocks or by specifying multiple exceptions within a singleexcept
block.Example:
try: # Some code that might raise exceptions pass except FileNotFoundError: print("File not found") except ZeroDivisionError: print("You can't divide by zero!")
Alternatively:
try: # Some code that might raise exceptions pass except (FileNotFoundError, ZeroDivisionError) as error: print(f"Caught an exception: {error}")
else
The
else
block can be used in conjunction with thetry
andexcept
blocks to specify code that should execute if thetry
block does not raise an exception.Example:
try: x = 1 / 2 except ZeroDivisionError: print("You can't divide by zero!") else: print("No exception occurred")
finally
The
finally
block allows you to specify code that should always execute, regardless of whether an exception was raised or not. This is often used for cleanup tasks, such as closing files or network connections.Example:
try: x = 1 / 0 except ZeroDivisionError: print("You can't divide by zero!") finally: print("This block will always execute")
By using error and exception handling techniques, you can make your Python programs more robust and handle unexpected situations gracefully, preventing your program from crashing and providing useful feedback to users or developers.
Common Errors
Python errors can be categorized into two main types: syntax errors and exceptions. Here are some common Python errors that you may encounter while programming:
SyntaxError: This occurs when the Python parser is unable to understand your code due to incorrect syntax. Syntax errors are usually easy to fix, as the interpreter points out the line where the error occurred and provides a description of the problem.
Example:
if x = 5: print("x is 5")
In this example, the
=
should be replaced with==
for the code to work properly.IndentationError: This occurs when your code has incorrect indentation. Python uses indentation to identify code blocks, so it's essential to maintain consistent indentation throughout your code.
Example:
def my_function(): print("Hello, world!")
In this example, the
print
statement should be indented to be inside the function.NameError: This occurs when you try to use a variable or function that hasn't been defined yet or is out of scope.
Example:
print(my_variable)
In this example,
my_variable
has not been defined before it's used.TypeError: This occurs when you try to perform an operation on incompatible data types or pass the wrong number of arguments to a function.
Example:
result = "hello" + 5
In this example, you're trying to add a string and an integer, which is not allowed in Python.
ValueError: This occurs when you pass an argument with the correct data type but an inappropriate value to a function.
Example:
number = int("hello")
In this example, the
int()
function expects a string that can be converted to an integer, but "hello" cannot be converted.IndexError: This occurs when you try to access an index that is out of bounds for a sequence (list, tuple, or string).
Example:
my_list = [1, 2, 3] print(my_list[3])
In this example, the highest valid index for
my_list
is 2, but you're trying to access index 3.KeyError: This occurs when you try to access a key that does not exist in a dictionary.
Example:
my_dict = {"a": 1, "b": 2} print(my_dict["c"])
In this example, the key "c" does not exist in
my_dict
.AttributeError: This occurs when you try to access an attribute or method that does not exist on an object.
Example:
my_list = [1, 2, 3] my_list.appendx(4)
In this example, the method
appendx
does not exist for lists; the correct method isappend
.ZeroDivisionError: This occurs when you try to divide a number by zero.
Example:
result = 5 / 0
In this example, you're attempting to divide 5 by 0, which is not allowed.
FileNotFoundError: This occurs when you try to open a file that does not exist.
Example:
with open("non_existent_file.txt", "r") as f:
content = f.read()
In this example, the file "non_existent_file.txt" does not exist, so Python raises a FileNotFoundError
.
These are just some of the common Python errors you might encounter while programming. Understanding the causes of these errors can help you write more robust and error-free code. When you encounter an error, carefully read the error message and traceback to identify the cause and location of the problem, and use this information to debug and fix your code.
Raising Exception
In Python, you can raise exceptions using the raise
statement. Raising an exception is useful when you want to signal that something unexpected or invalid has occurred in your program. By raising an exception, you can stop the normal execution of the code and transfer control to an appropriate exception handler (if one exists).
Here's an example of raising a custom exception with the raise
statement:
def divide(a, b):
if b == 0:
raise ValueError("You can't divide by zero!")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(e)
In this example, we define a function called divide
that takes two arguments, a
and b
. If b
is equal to 0, we raise a ValueError
with a custom error message. Then, we use a try
and except
block to call the divide
function and catch the raised exception.
When raising exceptions, it's a good practice to use built-in exception classes or create your own custom exception classes that inherit from the BaseException
or Exception
class. This allows you to provide more specific and meaningful error messages and makes it easier for other developers to understand and handle the exceptions in their code.
Here's an example of creating a custom exception class and raising it:
class DivisionByZeroError(Exception):
pass
def divide(a, b):
if b == 0:
raise DivisionByZeroError("You can't divide by zero!")
return a / b
try:
result = divide(10, 0)
except DivisionByZeroError as e:
print(e)
In this example, we define a custom exception class called DivisionByZeroError
that inherits from the Exception
class. We then raise this custom exception in the divide
function if b
is equal to 0. The try
and except
block is used to call the divide
function and catch the raised DivisionByZeroError
exception.
Raising exceptions with the raise
statement allows you to signal errors in your program and provide meaningful error messages. By using exception handling mechanisms such as try
and except
, you can create more robust and fault-tolerant programs.
Creating User-Defined Exception
Creating and using custom exceptions in Python is a great way to handle specific situations in your code that are not covered by built-in exceptions. Custom exceptions can provide more descriptive and meaningful error messages and make it easier for developers to understand and handle errors in their code.
To create a custom exception, you need to define a new class that inherits from the Exception
class or one of its subclasses. You can add custom attributes or methods to your exception class if needed.
Here's an example of creating a custom exception class:
class MyCustomError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return f"MyCustomError: {self.message}"
In this example, we define a custom exception class called MyCustomError
that inherits from the Exception
class. We override the __init__
method to accept a message
argument and store it as an instance attribute. We also override the __str__
method to provide a custom string representation of the exception.
Now let's see how to use this custom exception in our code:
def my_function(x):
if x < 0:
raise MyCustomError("x should be non-negative")
return x * 2
try:
result = my_function(-5)
except MyCustomError as e:
print(e)
In this example, we define a function called my_function
that takes a single argument, x
. If x
is negative, we raise our custom exception, MyCustomError
, with a specific error message. We then use a try
and except
block to call my_function
and catch the raised custom exception.
By creating and using custom exceptions, you can handle specific error situations in your code that are not covered by built-in exceptions. Custom exceptions allow you to provide more descriptive and meaningful error messages, making it easier for developers to understand and handle errors in their code.
__name__ == "__main__"
In Python, the __name__
variable is a special built-in variable that holds the name of the current module. When you run a Python script as the main program (i.e., not as a module imported by another script), the __name__
variable is set to the string "__main__"
.
By using the condition __name__ == "__main__"
, you can check if your script is being run as the main program or is being imported as a module by another script. This allows you to write code that can be used both as a standalone program and as an importable module without causing unintended side effects.
Here's an example:
# my_module.py
def main_function():
print("This is the main function.")
def helper_function():
print("This is a helper function.")
if __name__ == "__main__":
main_function()
In this example, if you run my_module.py
as a standalone program, the __name__
variable will be set to "__main__"
, and the main_function
will be called, producing the output:
This is the main function.
However, if you import my_module
into another script, the __name__
variable will be set to the module's name ("my_module"
), and the code inside the if __name__ == "__main__":
block will not be executed. This allows you to use helper_function
and any other functions in my_module
without automatically running main_function
.
Here's an example of importing my_module
:
# another_script.py
import my_module
my_module.helper_function()
When you run another_script.py
, the output will be:
This is a helper function.
The main_function
from my_module
is not called in this case, as the __name__
variable in my_module
is not equal to "__main__"
when it is imported.
In summary, the __name__ == "__main__"
condition is used to determine if a Python script is being run as the main program or being imported as a module. This allows you to write code that can be used both as a standalone program and as an importable module without causing unintended side effects.
File Handling
File handling is an essential part of any programming language, including Python. It allows you to work with files, such as reading data from them, writing new data, or modifying existing data. Python provides built-in functions and methods for easy file handling.
Here's a brief overview of common file-handling operations in Python:
Opening a file: You can open a file using the built-in
open()
function, which returns a file object. The function takes two arguments: the file path and the mode in which the file should be opened (e.g., 'r' for reading, 'w' for writing, 'a' for appending).Example:
file = open("example.txt", "r")
Reading from a file: Once a file is opened in read mode ('r'), you can read its contents using methods like
read()
,readline()
, orreadlines()
.Examples:
content = file.read() # Read the entire file line = file.readline() # Read a single line lines = file.readlines() # Read all lines as a list
Writing to a file: To write to a file, open it in write mode ('w') or append mode ('a'). Then, use the
write()
method to write content to the file. Be aware that opening a file in write mode will overwrite its contents.Example:
file = open("output.txt", "w") file.write("Hello, world!")
Closing a file: After finishing your work with a file, it's important to close it using the
close()
method. This releases the resources associated with the file and ensures that any changes are saved.Example:
file.close()
Using a context manager: A more Pythonic way to work with files is by using a context manager (
with
statement). This automatically handles file closing, even if an exception occurs during file operations.Example:
with open("example.txt", "r") as file: content = file.read() # The file is automatically closed after the with block.
Handling exceptions: While working with files, you may encounter exceptions like
FileNotFoundError
orPermissionError
. It's a good practice to handle these exceptions usingtry
andexcept
blocks to prevent your program from crashing.Example:
try: with open("non_existent_file.txt", "r") as file: content = file.read() except FileNotFoundError: print("The file does not exist.")
These are the basics of file handling in Python. By using the built-in functions and methods, you can easily read from, write to, and manage files in your Python programs. Remember to always close your files when you're done with them and handle any exceptions that may occur during file operations.
Python Package Management System
PyPI, short for Python Package Index, is the official repository for Python software packages. It is a central hub where developers can share and distribute their Python packages, making it easy for others to discover and install them. PyPI is sometimes also referred to as the "Cheese Shop," a reference to a Monty Python sketch.
You can access PyPI through its website (https://pypi.org/) to search for packages, view their documentation, and find other relevant information. However, the most common way to interact with PyPI is through package management tools like pip
(Python's default package installer) or conda
(part of the Anaconda Python distribution).
To install a package from PyPI using pip
, you can use the following command:
pip install package_name
For example, to install the popular requests
package, you would run:
pip install requests
Once installed, you can use the package in your Python scripts by importing it. For example:
import requests
response = requests.get('https://api.github.com')
print(response.json())
In addition to installing packages, pip
also allows you to manage your installed packages. Some common operations include:
Listing installed packages:
pip list
Uninstalling a package:
pip uninstall package_name
Upgrading a package:
pip install --upgrade package_name
It's important to note that not all packages on PyPI are guaranteed to be high-quality, secure, or up-to-date. Before using a package, it's a good idea to check its documentation, user ratings, and recent updates to ensure it's a good fit for your project.
In summary, PyPI is the official repository for Python software packages, making it easy for developers to share and distribute their work. You can interact with PyPI through package management tools like pip
to install, manage, and use packages in your Python projects.
Recursion
Recursion is a technique in programming where a function calls itself directly or indirectly to solve a problem. It is particularly useful for problems that can be broken down into smaller subproblems of the same type. The process continues until a base case is reached, which is a condition where the function no longer calls itself and returns a value directly.
In Python, recursion can be implemented using a function that calls itself within its definition. Let's take a look at a simple example: calculating the factorial of a number. The factorial of a number n (denoted as n!) is the product of all positive integers less than or equal to n. For example, 5! = 5 * 4 * 3 * 2 * 1 = 120.
Here's a recursive function to calculate the factorial in Python:
def factorial(n):
# Base case: when n is 0 or 1, the factorial is 1
if n == 0 or n == 1:
return 1
# Recursive case: n! = n * (n-1)!
return n * factorial(n - 1)
Let's break down how the function works:
When the function is called with
n
equal to 0 or 1, it returns 1. This is the base case and it prevents the function from calling itself indefinitely.If
n
is greater than 1, the function calls itself with the argumentn - 1
. This breaks the problem into a smaller subproblem (calculating the factorial ofn - 1
).The function continues to call itself until the base case is reached. At that point, it starts returning values up the chain of recursive calls.
The final result is calculated by multiplying all returned values together.
Here's an example of how the recursion unfolds for calculating 5!:
factorial(5)
5 * factorial(4)
5 * (4 * factorial(3))
5 * (4 * (3 * factorial(2)))
5 * (4 * (3 * (2 * factorial(1))))
5 * (4 * (3 * (2 * 1)))
5 * (4 * (3 * 2))
5 * (4 * 6)
5 * 24
120
Recursion can be an elegant and concise way to solve certain problems, but it can also lead to a high amount of memory usage and potential stack overflow errors for deep recursion. In such cases, an iterative approach may be more efficient.
Map, Filter and Reduce
Lambda Functions
A lambda function in Python is a small, anonymous function that can be defined using the lambda
keyword. It can have any number of arguments but only a single expression. Lambda functions are often used for short operations that are simple enough not to require a full function definition using the def
keyword.
The syntax for a lambda function is as follows:
lambda arguments: expression
Here's an example to demonstrate the usage of a lambda function in Python:
Suppose you want to create a small function to square a given number. You can use a lambda function like this:
square = lambda x: x * x
# Test the lambda function
result = square(5)
print(result) # Output: 25
In this example, the lambda function takes one argument x
and returns the result of x * x
. We assign the lambda function to the variable square
, which can then be used like any other function.
Keep in mind that lambda functions are limited in their functionality and can't include complex logic or multiple expressions. For more complex operations, it's better to use a regular function defined with the def
keyword.
Map
The map
function in Python is a built-in function that applies a given function to each item of an iterable (e.g., list, tuple) and returns a map object, which can be converted to a list or other iterable types. It's often used with lambda functions for simple and concise code.
Syntax: map(function, iterable)
Here are some examples to demonstrate the usage of the map
function in Python:
Example 1: Use map
to square all elements of a list:
numbers = [1, 2, 3, 4, 5]
# Use a lambda function to square each number
squared_numbers = map(lambda x: x * x, numbers)
# Convert the result to a list
squared_numbers_list = list(squared_numbers)
print(squared_numbers_list) # Output: [1, 4, 9, 16, 25]
Example 2: Use map
to convert a list of strings to uppercase:
words = ["hello", "world", "python"]
# Use the `str.upper` function to convert each string to uppercase
uppercase_words = map(str.upper, words)
# Convert the result to a list
uppercase_words_list = list(uppercase_words)
print(uppercase_words_list) # Output: ['HELLO', 'WORLD', 'PYTHON']
Example 3: Use map
to calculate the length of each string in a list:
words = ["apple", "banana", "cherry"]
# Use a lambda function to calculate the length of each string
lengths = map(lambda x: len(x), words)
# Convert the result to a list
lengths_list = list(lengths)
print(lengths_list) # Output: [5, 6, 6]
In each example, the map
function applies the specified function (a lambda function or a built-in function) to each element of the input iterable. The results are collected in a map object, which is then converted to a list for easy access and display.
Filter
The filter
function in Python is a built-in function that filters elements from an iterable (e.g., list, tuple) based on a given function, which should return a boolean value. The filter
function returns a filter object, which can be converted to a list or other iterable types.
Syntax: filter(function, iterable)
Here are some examples to demonstrate the usage of the filter
function in Python:
Example 1: Use filter
to get even numbers from a list:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Use a lambda function to filter even numbers
even_numbers = filter(lambda x: x % 2 == 0, numbers)
# Convert the result to a list
even_numbers_list = list(even_numbers)
print(even_numbers_list) # Output: [2, 4, 6, 8]
Example 2: Use filter
to remove empty strings from a list:
strings = ["apple", "", "banana", "", "cherry"]
# Use a lambda function to filter out empty strings
non_empty_strings = filter(lambda x: x != "", strings)
# Convert the result to a list
non_empty_strings_list = list(non_empty_strings)
print(non_empty_strings_list) # Output: ['apple', 'banana', 'cherry']
Example 3: Use filter
to get words with more than 3 characters from a list:
words = ["apple", "it", "banana", "car", "cherry"]
# Use a lambda function to filter words with more than 3 characters
long_words = filter(lambda x: len(x) > 3, words)
# Convert the result to a list
long_words_list = list(long_words)
print(long_words_list) # Output: ['apple', 'banana', 'cherry']
In each example, the filter
function applies the specified function (usually a lambda function) to each element of the input iterable. The function should return a boolean value, and only the elements for which the function returns True
are retained in the output. The results are collected in a filter object, which is then converted to a list for easy access and display.
Reduce
The reduce
function in Python is a higher-order function that applies a given function cumulatively to the elements of an iterable (e.g., list, tuple), from left to right, so as to reduce the iterable to a single value.
Syntax: reduce(function, iterable[, initializer])
To use the reduce
function, you need to import it from the functools
module.
Here are some examples to demonstrate the usage of the reduce
function in Python:
Example 1: Use reduce
to calculate the product of all elements in a list:
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# Use a lambda function to calculate the product
product = reduce(lambda x, y: x * y, numbers)
print(product) # Output: 120
Example 2: Use reduce
to find the largest number in a list:
from functools import reduce
numbers = [34, 65, 12, 76, 25]
# Use a lambda function to find the largest number
largest_number = reduce(lambda x, y: x if x > y else y, numbers)
print(largest_number) # Output: 76
Example 3: Use reduce
to concatenate a list of strings:
from functools import reduce
words = ["Hello", " ", "World", "!"]
# Use a lambda function to concatenate the strings
concatenated_string = reduce(lambda x, y: x + y, words)
print(concatenated_string) # Output: "Hello World!"
In each example, the reduce
function applies the specified lambda function to the elements of the input iterable, reducing the iterable to a single value. The lambda function takes two arguments, and reduce
applies it cumulatively to the elements, from left to right. If an optional initializer
is provided, it is used as the initial value for the accumulation and is placed before the items of the iterable in the calculation.
List Comprehension
List comprehension is a concise way to create lists in Python. It's a syntactic construct that allows you to create a new list by specifying the elements you want to include, using a single line of code. List comprehensions are often more readable and faster than using a loop to create the same list.
The general syntax for a list comprehension is as follows:
[expression for item in iterable if condition]
Here are some examples to demonstrate the usage of list comprehension in Python:
Example 1: Create a list of squares of numbers from 0 to 9:
squares = [x * x for x in range(10)]
print(squares) # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Example 2: Create a list of even numbers from 1 to 20:
even_numbers = [x for x in range(1, 21) if x % 2 == 0]
print(even_numbers) # Output: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Example 3: Convert a list of strings to uppercase:
words = ["hello", "world", "python"]
uppercase_words = [word.upper() for word in words]
print(uppercase_words) # Output: ['HELLO', 'WORLD', 'PYTHON']
Example 4: Create a list of tuples containing a number and its square:
number_squares = [(x, x * x) for x in range(1, 6)]
print(number_squares) # Output: [(1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
In each example, a new list is created using a list comprehension. The expression specifies what each element of the new list should look like, and the for
loop iterates over the input iterable. An optional if
condition can be added to filter the elements based on a specific criterion.
Regular Expressions
Regular Expressions (or regex) are a powerful tool for pattern matching and text manipulation in Python. A regex is a sequence of characters that defines a search pattern. It's used to match and manipulate strings, based on a set of rules that you define.
Here's an explanation of the most commonly used regex syntax with examples:
- Character classes: Character classes match a single character from a set of characters. You can use square brackets to define the set of characters you want to match.
Example: Match any vowel in a string:
import re
string = "hello world"
vowels = re.findall("[aeiou]", string)
print(vowels) # Output: ['e', 'o', 'o']
- Quantifiers: Quantifiers match a sequence of characters that occurs a certain number of times. You can use curly braces to specify the exact number of times, or use shorthand symbols to specify a range of times.
Example: Match any sequence of 3 digits in a string:
import re
string = "hello 123 world 456"
numbers = re.findall("\d{3}", string)
print(numbers) # Output: ['123', '456']
- Anchors: Anchors match a specific position in a string. You can use the caret symbol to match the start of a string, and the dollar sign to match the end of a string.
Example: Match any string that starts with "hello":
import re
string1 = "hello world"
string2 = "world hello"
pattern = "^hello"
match1 = re.findall(pattern, string1)
match2 = re.findall(pattern, string2)
print(match1) # Output: ['hello']
print(match2) # Output: []
- Alternation: Alternation matches any of several possible alternatives. You can use the vertical bar symbol to separate the alternatives.
Example: Match any string that contains either "hello" or "world":
import re
string1 = "hello world"
string2 = "goodbye python"
pattern = "hello|world"
match1 = re.findall(pattern, string1)
match2 = re.findall(pattern, string2)
print(match1) # Output: ['hello', 'world']
print(match2) # Output: []
- Groups: Groups capture a sub-pattern within a larger pattern. You can use parentheses to group the sub-pattern.
Example: Extract the date from a string in the format "dd-mm-yyyy":
import re
string = "Today is 27-04-2023"
pattern = "(\d{2})-(\d{2})-(\d{4})"
match = re.search(pattern, string)
if match:
day = match.group(1)
month = match.group(2)
year = match.group(3)
print("Day:", day)
print("Month:", month)
print("Year:", year)
In this example, the search
function is used to find the first occurrence of the pattern in the string. The pattern contains three groups, each matching a two-digit day, a two-digit month, and a four-digit year. The group
method is used to extract the matched sub-patterns.
These are just a few examples of the many capabilities of regular expressions. Regex can be very powerful for text manipulation, but they can also be complex and difficult to read. It's important to practice and test your regex patterns to make sure they match what you expect.
Common Patterns
Here are some of the most common regular expression patterns used in Python, along with examples:
- Matching a specific string: To match a specific string, use the string itself as the regular expression pattern.
Example:
import re
string = "Hello, world!"
pattern = "Hello"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match the string "Hello" in the input string.
- Matching any character: To match any character, use the dot symbol (
.
) as the regular expression pattern.
Example:
import re
string = "Hello, world!"
pattern = "w.rld"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match the string "world" in the input string, where the dot symbol matches any single character.
- Matching any character in a set: To match any character in a set, use square brackets to define the set of characters you want to match.
Example:
import re
string = "Hello, world!"
pattern = "[aeiou]"
matches = re.findall(pattern, string)
print(matches)
This will match any vowel in the input string, where the pattern [aeiou]
matches any character that is either "a", "e", "i", "o", or "u".
- Matching any character not in a set: To match any character not in a set, use square brackets and a caret symbol (
^
) to define the set of characters you don't want to match.
Example:
import re
string = "Hello, world!"
pattern = "[^aeiou]"
matches = re.findall(pattern, string)
print(matches)
This will match any non-vowel character in the input string, where the pattern [^aeiou]
matches any character that is not "a", "e", "i", "o", or "u".
- Matching a word boundary: To match a word boundary, use the backslash (
\b
) as the regular expression pattern.
Example:
import re
string = "Hello, world!"
pattern = r"\bworld\b"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match the string "world" in the input string, where the pattern \bworld\b
matches the word "world" surrounded by word boundaries.
- Matching one or more occurrences: To match one or more occurrences of a pattern, use the plus symbol (
+
) as the regular expression pattern.
Example:
import re
string = "Hello, world!"
pattern = r"\w+"
matches = re.findall(pattern, string)
print(matches)
This will match any sequence of one or more word characters in the input string, where the pattern \w+
matches any character that is a letter, digit, or underscore, one or more times.
- Matching zero or more occurrences: To match zero or more occurrences of a pattern, use the asterisk symbol (
*
) as the regular expression pattern.
Example:
import re
string = "Hello, world!"
pattern = "l*o"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match any sequence of zero or more "l" characters followed by an "o" character in the input string, where the pattern l*o
matches any number of "l" characters, followed by an "o" character.
- Matching a specific number of occurrences: To match a specific number of occurrences of a pattern, use curly braces (
{}
) to specify the exact number of occurrences.
Example:
import re
string = "baaaad"
pattern = "a{3}"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match any sequence of exactly three "a" characters in the input string, where the pattern a{3}
matches an "a" character exactly three times.
- Matching a range of occurrences: To match a range of occurrences of a pattern, use curly braces (
{}
) and a comma to specify a minimum and maximum number of occurrences.
Example:
import re
string = "baaaad"
pattern = "a{2,4}"
match = re.search(pattern, string)
if match:
print("Match found")
else:
print("Match not found")
This will match any sequence of two to four "a" characters in the input string, where the pattern a{2,4}
matches an "a" character between two and four times.
These are just a few examples of the many regular expression patterns available in Python. Regular expressions are a powerful tool for pattern matching and text manipulation, and can be used to solve a wide range of problems in programming.
Decorators
Decorators are a powerful feature in Python that allows you to modify or extend the behavior of functions or methods. They are essentially functions that take another function as input and return a new function, usually extending or modifying the behavior of the input function.
Here's an example of a simple decorator and how to use it:
- Define a decorator function:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
In this example, my_decorator
is a decorator function. It takes a function func
as an argument, defines a new function wrapper
that calls func
and adds some behavior before and after the call, and returns the wrapper
function.
- Use the decorator to modify a function:
def say_hello():
print("Hello!")
# Decorate the `say_hello` function
decorated_say_hello = my_decorator(say_hello)
# Call the decorated function
decorated_say_hello()
Output:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
- Alternatively, you can use the
@decorator
syntax to apply a decorator to a function:
@my_decorator
def say_hello():
print("Hello!")
# Call the decorated function
say_hello()
This code produces the same output as before. The @my_decorator
syntax is just a more convenient way of applying the decorator to the say_hello
function.
Here's another example of a decorator that takes arguments:
def repeat_decorator(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat_decorator(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Hello, Alice!
Hello, Alice!
Hello, Alice!
In this example, repeat_decorator
is a decorator factory that takes an argument num_times
and returns a decorator decorator_repeat
. The decorator_repeat
function takes a function func
as input, defines a new function wrapper
that calls func
num_times
times, and returns the wrapper
function. The greet
function is decorated with repeat_decorator(3)
, so it will be called three times when invoked.
Logging
Logging is a means of tracking events that occur in your software. In Python, the logging
module provides a flexible framework for emitting log messages from your applications. Logging can help you identify issues, debug your code, and understand the flow of your application.
Here's a simple example of using Python's logging module:
- Import the logging module:
import logging
- Configure the logging:
# Basic configuration with level set to DEBUG and logs printed to the console
logging.basicConfig(level=logging.DEBUG)
- Log messages using different logging levels:
logging.debug("This is a debug message")
logging.info("This is an informational message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
These are the five standard logging levels provided by the logging module:
DEBUG: Detailed information, typically of interest only when diagnosing problems.
INFO: Confirmation that things are working as expected.
WARNING: An indication that something unexpected happened or indicative of some problem in the near future (e.g., 'disk space low'). The software is still working as expected.
ERROR: Due to a more serious problem, the software has not been able to perform some function.
CRITICAL: A very serious error, indicating that the program itself may be unable to continue running.
Output:
DEBUG:root:This is a debug message
INFO:root:This is an informational message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message
Here's another example with more advanced configurations:
import logging
# Create a custom logger
logger = logging.getLogger('my_logger')
# Set the logging level
logger.setLevel(logging.DEBUG)
# Create a file handler to log messages to a file
file_handler = logging.FileHandler('my_log.log')
# Create a custom log format
log_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# Set the format for the file handler
file_handler.setFormatter(log_format)
# Add the file handler to the logger
logger.addHandler(file_handler)
# Log messages
logger.debug("This is a debug message")
logger.info("This is an informational message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")
This example creates a custom logger named my_logger
with a logging level set to DEBUG. It logs messages to a file called my_log.log
using a custom log format. The log messages will be similar to the previous example, but they will be written to the my_log.log
file instead of printed to the console.
Date and Time
In Python, the datetime
module provides classes for manipulating dates and times. The most commonly used classes in this module are datetime
, date
, time
, timedelta
, and timezone
.
Here are some examples to illustrate how to work with dates and times in Python:
- Import the
datetime
module:
import datetime
- Get the current date and time:
current_datetime = datetime.datetime.now()
print(current_datetime)
Output (example):
2023-04-28 13:00:00.123456
- Get the current date:
current_date = datetime.date.today()
print(current_date)
Output (example):
2023-04-28
- Create a specific date and time:
some_datetime = datetime.datetime(2022, 12, 25, 10, 30)
print(some_datetime)
Output:
2022-12-25 10:30:00
- Create a specific date:
some_date = datetime.date(2022, 12, 25)
print(some_date)
Output:
2022-12-25
- Create a specific time:
some_time = datetime.time(10, 30)
print(some_time)
Output:
10:30:00
- Calculate the difference between two dates:
date1 = datetime.date(2023, 1, 1)
date2 = datetime.date(2023, 4, 28)
delta = date2 - date1
print(delta)
Output:
117 days, 0:00:00
- Add or subtract a
timedelta
from a date or datetime:
today = datetime.date.today()
one_week = datetime.timedelta(weeks=1)
next_week = today + one_week
print(next_week)
Output (example):
2023-05-05
- Format a date or datetime as a string:
current_datetime = datetime.datetime.now()
formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S")
print(formatted_datetime)
Output (example):
2023-04-28 13:00:00
- Parse a date or datetime from a string:
date_string = "2022-12-25"
parsed_date = datetime.datetime.strptime(date_string, "%Y-%m-%d")
print(parsed_date)
Output:
2022-12-25 00:00:00
These examples demonstrate some of the basic operations you can perform with dates and times in Python using the datetime
module. For more advanced operations, you can use the dateutil
library, which extends the functionality of the datetime
module.