Python language Basics
BCA-302: Python Programming Notes
Python Concepts
- Origin: Python was created by Guido van Rossum in the late 1980s, with its first release in 1991. It was conceived as a successor to the ABC programming language, with a focus on code readability and a clear syntax. The name "Python" was inspired by the Monty Python's Flying Circus television show.
- Comparison: Python is often compared to other languages like Java, C++, and JavaScript. Key differences include:
- Syntax: Python emphasizes readability with a clean, English-like syntax, using indentation for block delimitation. Java and C++ use braces `{}`. JavaScript is more flexible but can lead to less readable code if not styled carefully.
- Typing: Python is dynamically typed (you don't need to declare variable types), while Java and C++ are statically typed (you must declare types). JavaScript is also dynamically typed.
- Interpretation: Python is interpreted (code is executed line by line), while Java is compiled to bytecode and then interpreted by the JVM. C++ is compiled directly to machine code.
- Memory Management: Python uses automatic memory management (garbage collection), relieving the programmer from manual memory allocation/deallocation. C++ requires manual memory management, which can be a source of errors. Java also uses garbage collection.
- Use Cases: Python is widely used for web development, data science, scripting, AI, and more. Java is prevalent in enterprise applications. C++ is used for systems programming, game development, and high-performance computing. JavaScript is the primary language for front-end web development and is also used on the server-side (Node.js).
- Comments: Used to add explanations to code, improving readability and maintainability. Ignored by the Python interpreter.
- Single-line comment:
# This is a comment
- Multi-line comment (docstring):
"""This is a multi-line comment, often used as a docstring to document functions or classes."""
- Single-line comment:
- Variables and Assignment: Variables are names that store data values. Assignment uses the `=` operator to bind a value to a variable name.
x = 10 name = "Alice" pi = 3.14159
- Identifiers: Names given to variables, functions, classes, modules, etc.
- Rules:
- Must start with a letter (a-z, A-Z) or an underscore (\_).
- Can contain letters, numbers (0-9), and underscores.
- Case-sensitive (e.g., `myVar` and `myvar` are different).
- Cannot be a Python keyword (e.g., `if`, `for`, `while`, `def`, `class`).
- Best Practices:
- Use descriptive names that indicate the variable's purpose.
- Use lowercase with underscores for variable and function names (e.g., `my_variable`, `calculate_sum`). This is known as snake_case.
- Use CamelCase for class names (e.g., `MyClass`).
- Avoid single-character names (except for loop counters in simple cases).
- Rules:
- Basic Style Guidelines (PEP 8): PEP 8 is the style guide for Python code. It provides recommendations for:
- Indentation: Use 4 spaces per indentation level.
- Line length: Limit lines to 79 characters.
- Blank lines: Use blank lines to separate functions and classes, and to improve readability within functions.
- Naming conventions: As described in Identifiers.
- Whitespace: Use spaces around operators and after commas.
- Imports: Place imports at the beginning of the file, group them, and follow a specific order.
- Standard Types: Built-in data types that represent different kinds of values.
- Numbers:
- Integers (`int`): Whole numbers (e.g., `10`, `-5`, `0`).
x = 10 print(type(x)) # Output: <class 'int'>
- Floating-point numbers (`float`): Numbers with a decimal point (e.g., `3.14`, `-2.5`, `0.0`).
y = 3.14 print(type(y)) # Output: <class 'float'>
- Complex numbers (`complex`): Numbers with a real and imaginary part (e.g., `2+3j`, where `j` is the imaginary unit).
z = 2 + 3j print(type(z)) # Output: <class 'complex'>
- Integers (`int`): Whole numbers (e.g., `10`, `-5`, `0`).
- Strings (`str`): Immutable sequences of characters (e.g., `"Hello"`, `'World'`, `"""This is a multi-line string"""`).
message = "Hello, world!" print(type(message)) # Output: <class 'str'>
- Boolean (`bool`): Represents truth values: `True` or `False`.
is_valid = True print(type(is_valid)) # Output: <class 'bool'>
- Numbers:
- Internal Types: Types used internally by the Python interpreter. Programmers don't usually interact with these directly. Examples include types related to code objects, frames, and tracebacks.
- Operators: Symbols that perform operations on values (operands).
- Arithmetic operators: `+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), `//` (floor division - returns the integer part of the division), `%` (modulo - returns the remainder), `**` (exponentiation).
result = 5 + 2 # 7 result = 5 - 2 # 3 result = 5 * 2 # 10 result = 5 / 2 # 2.5 result = 5 // 2 # 2 result = 5 % 2 # 1 result = 5 ** 2 # 25
- Comparison operators: `==` (equal to), `!=` (not equal to), `>` (greater than), `<` (less than), `>=` (greater than or equal to), `<=` (less than or equal to). Return a Boolean value.
x = 5 y = 10 print(x == y) # False print(x != y) # True print(x > y) # False print(x < y) # True print(x >= y) # False print(x <= y) # True
- Logical operators: `and` (returns `True` if both operands are `True`), `or` (returns `True` if at least one operand is `True`), `not` (returns the opposite of the operand's Boolean value).
a = True b = False print(a and b) # False print(a or b) # True print(not a) # False
- Assignment operators: `=` (simple assignment), `+=`, `-=`, `*=`, `/=`, `//=`, `%=`, `**=` (compound assignment operators that combine an arithmetic operation with assignment).
x = 10 x += 5 # x is now 15 x -= 5 # x is now 10 x *= 5 # x is now 50 x /= 5 # x is now 10.0 x //= 3 # x is now 3.0 x %= 3 # x is now 0.0 x **= 2 # x is now 0.0
- Membership operators: `in` (returns `True` if a value is found in a sequence), `not in` (returns `True` if a value is not found in a sequence).
my_list = [1, 2, 3] print(2 in my_list) # True print(4 in my_list) # False print(4 not in my_list) # True
- Identity operators: `is` (returns `True` if two variables refer to the same object), `is not` (returns `True` if two variables do not refer to the same object).
x = [1, 2, 3] y = x z = [1, 2, 3] print(x is y) # True (x and y refer to the same object) print(x is z) # False (x and z are different objects, even with the same content) print(x is not z) # True
- Arithmetic operators: `+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), `//` (floor division - returns the integer part of the division), `%` (modulo - returns the remainder), `**` (exponentiation).
- Built-in Functions: Functions that are always available in Python without needing to import any modules. Examples: `print()`, `len()`, `type()`, `int()`, `float()`, `str()`, `bool()`, `input()`, `range()`, `sum()`, `max()`, `min()`.
print("Hello") length = len("Python") print(length) # Output: 6 data_type = type(10) print(data_type) # Output: <class 'int'> converted_int = int("123") print(converted_int) # Output: 123 converted_float = float("123.45") print(converted_float) # Output: 123.45 text = str(123) print(text) # Output: "123" value = bool(0) print(value) # Output: False user_input = input("Enter your name: ") print("Hello, " + user_input) numbers = range(5) print(list(numbers)) # Output: [0, 1, 2, 3, 4] total = sum([1, 2, 3, 4]) print(total) # Output: 10 maximum = max([1, 5, 2, 8]) print(maximum) # Output: 8 minimum = min([1, 5, 2, 8]) print(minimum) # Output: 1
- Numbers and Strings: See "Standard Types" above for more details.
- Sequences: Ordered collections of items.
- Strings (`str`): Immutable sequences of characters.
- Lists (`list`): Mutable sequences of items (can contain items of different types).
- Tuples (`tuple`): Immutable sequences of items.
- String Operators & Functions:
- Concatenation (`+`): Joins two strings.
str1 = "Hello" str2 = "World" result = str1 + " " + str2 # "Hello World"
- Repetition (`*`): Repeats a string multiple times.
text = "abc" repeated_text = text * 3 # "abcabcabc"
- Slicing (`[start:end:step]`): Extracts a portion of a string.
my_string = "Python" substring = my_string[1:4] # "yth" substring = my_string[:3] # "Pyt" substring = my_string[2:] # "thon" substring = my_string[::2] # "Pto" substring = my_string[::-1] # "nohtyP"
- Methods: Functions that are called on a string object (e.g., `.upper()`, `.lower()`, `.strip()`, `.find()`, `.replace()`, `.split()`, `.join()`, `.format()`).
text = " Python is Fun " upper_text = text.upper() # " PYTHON IS FUN " lower_text = text.lower() # " python is fun " stripped_text = text.strip() # "Python is Fun" index = text.find("Fun") # 11 replaced_text = text.replace("Fun", "Awesome") # " Python is Awesome " words = text.split() # ['Python', 'is', 'Fun'] joined_text = "-".join(words) # "Python-is-Fun" formatted_text = "{} is {}".format("Python", "Awesome") # "Python is Awesome" f_string = f"{'Python'} is {'Awesome'}" # "Python is Awesome"
- Concatenation (`+`): Joins two strings.
- Special Features of Strings:
- Immutability: Strings cannot be changed after they are created. Operations that appear to modify a string actually create a new string.
- String methods: Provide powerful ways to manipulate and work with string data.
- Formatted string literals (f-strings): A concise way to embed expressions inside string literals.
- Memory Management: Python uses automatic memory management through a process called garbage collection. The interpreter automatically allocates memory for objects and reclaims memory that is no longer being used, reducing the risk of memory leaks and other memory-related errors. Python also has a small object allocator for frequently used small objects, which improves efficiency.
Conditionals and Loops
- `if` statement: Executes a block of code if a condition is `True`. The condition is an expression that evaluates to a Boolean value.
x = 15 if x > 0: print("Positive")
- `else` statement: Provides an alternative block of code to execute if the `if` condition is `False`.
x = 10 if x % 2 == 0: print("Even") else: print("Odd")
- `elif` statement: Short for "else if." Allows you to check multiple conditions in sequence.
grade = 85 if grade >= 90: print("A") elif grade >= 80: print("B") elif grade >= 70: print("C") else: print("Below C")
- `while` statement: Repeatedly executes a block of code as long as a condition is `True`. It's used for indefinite iteration (when you don't know in advance how many times the loop will execute).
count = 0 while count < 5: print(count) count += 1
- `for` statement: Iterates over a sequence (e.g., list, tuple, string, range) and executes a block of code for each item in the sequence. It's used for definite iteration (when you know how many times the loop will execute).
fruits = ["apple", "banana", "cherry"] for fruit in fruits: print(fruit)
- `break` statement: Immediately terminates the innermost loop (either `for` or `while`) and execution continues with the statement after the loop.
for i in range(10): if i == 5: break print(i)
- `continue` statement: Skips the rest of the current iteration of the loop and proceeds to the next iteration.
for i in range(5): if i == 3: continue print(i)
- `pass` statement: A null operation; it does nothing. It's used as a placeholder where a statement is syntactically required but no code needs to be executed.
if True: pass # Placeholder
- `else` statement with loop:
- In a `for` loop, the `else` block is executed after the loop completes normally (i.e., without encountering a `break` statement).
for i in range(3): print(i) else: print("Loop finished normally")
- In a `while` loop, the `else` block is executed when the loop condition becomes `False`.
count = 0 while count < 3: print(count) count += 1 else: print("While loop finished")
- In a `for` loop, the `else` block is executed after the loop completes normally (i.e., without encountering a `break` statement).
Object and Classes
- Classes in Python, Principles of Object Orientation, Creating Classes, Instance Methods, Class variables, Inheritance, Polymorphism. Type Identification, Python libraries(Strings. Data structures & algorithms )
- Classes: Blueprints for creating objects.
class Dog: def __init__(self, name, breed): self.name = name self.breed = breed def bark(self): print("Woof!") my_dog = Dog("Buddy", "Golden Retriever") print(my_dog.name) # Output: Buddy my_dog.bark() # Output: Woof!
- Principles of Object Orientation:
- Encapsulation: Bundling data (attributes) and methods that operate on the data within a single unit (class).
- Abstraction: Hiding complex implementation details and showing only necessary information to the user.
- Inheritance: Creating new classes (derived or child classes) based on existing classes (base or parent classes), inheriting their attributes and methods.
- Polymorphism: The ability of objects of different classes to respond to the same method call in their own way.
- Creating Classes: Using the `class` keyword. The `__init__` method is a special constructor.
- Instance Methods: Methods that operate on the instance (object) of a class. They have `self` as the first parameter.
- Class Variables: Variables that are shared by all instances of a class. They are defined within the class but outside any methods.
class Counter: count = 0 # Class variable def __init__(self): Counter.count += 1 c1 = Counter() c2 = Counter() print(Counter.count) # Output: 2
- Inheritance: Creating a new class that inherits from an existing class.
class Animal: def speak(self): print("Generic animal sound") class Cat(Animal): def speak(self): # Method overriding print("Meow!") my_cat = Cat() my_cat.speak() # Output: Meow!
- Polymorphism: Different classes responding to the same method.
def animal_sound(animal): animal.speak() animal1 = Animal() animal2 = Cat() animal_sound(animal1) # Output: Generic animal sound animal_sound(animal2) # Output: Meow!
- Type Identification: Using functions like `type()` and `isinstance()` to check the type of an object.
x = 10 print(type(x)) # <class 'int'> print(isinstance(x, int)) # True print(isinstance(x, float)) # False
- Python libraries(Strings. Data structures & algorithms )
Lists and Sets
- Built-in Functions, List type built in Methods. Tuples. Tuple Operators Special Features of Tuples, Set: Introduction, Accessing, Built-in Methods (Add, Update, Clear, Copy, Discard, Remove), Operations (Union, Intersection, Difference )
- Lists: Mutable ordered sequences (already covered in Unit-I).
- Sets: Unordered collections of unique elements, defined using curly braces `{}` or the `set()` constructor.
my_set = {1, 2, 3, 3, 4} # {1, 2, 3, 4} (duplicates are automatically removed) another_set = set([4, 5, 6])
- Built-in Functions (for Lists and Sets): `len()`, `max()`, `min()`, `sorted()`, `sum()`.
my_list = [3, 1, 4, 1, 5, 9, 2, 6] print(len(my_list)) # 8 print(max(my_list)) # 9 print(min(my_list)) # 1 print(sorted(my_list)) # [1, 1, 2, 3, 4, 5, 6, 9] print(sum(my_list)) # 31
- Tuples: Immutable ordered sequences (already covered in Unit-I).
- Tuple Operators: Similar to list operators (concatenation `+`, repetition `*`).
tuple1 = (1, 2, 3) tuple2 = (4, 5, 6) combined_tuple = tuple1 + tuple2 # (1, 2, 3, 4, 5, 6) repeated_tuple = tuple1 * 2 # (1, 2, 3, 1, 2, 3)
- Special Features of Tuples: Immutability makes them suitable for representing fixed collections of items and as keys in dictionaries.
- Set Introduction: Already covered above.
- Accessing: You can iterate through sets using a `for` loop, but you cannot access elements by index because they are unordered.
for item in my_set: print(item)
- Built-in Methods (Sets):
- `add(element)`: Adds an element to the set.
my_set.add(10)
- `update(iterable)`: Adds multiple elements from an iterable.
my_set.update([7, 8, 9])
- `clear()`: Removes all elements from the set.
my_set.clear()
- `copy()`: Returns a shallow copy of the set.
new_set = my_set.copy()
- `discard(element)`: Removes an element if it is present (no error if not found).
my_set.discard(3)
- `remove(element)`: Removes an element (raises a `KeyError` if not found).
my_set.remove(2)
- `add(element)`: Adds an element to the set.
- Operations (Sets):
- Union (`|` or `union()`): Returns a new set containing all elements from both sets.
set1 = {1, 2, 3} set2 = {3, 4, 5} union_set = set1 | set2 # {1, 2, 3, 4, 5} union_set = set1.union(set2)
- Intersection (`&` or `intersection()`): Returns a new set containing common elements.
intersection_set = set1 & set2 # {3} intersection_set = set1.intersection(set2)
- Difference (`-` or `difference()`): Returns a new set containing elements in the first set but not in the second.
difference_set = set1 - set2 # {1, 2} difference_set = set1.difference(set2)
- Union (`|` or `union()`): Returns a new set containing all elements from both sets.
- Dictionaries: Introduction to Dictionaries, Built-in Functions, Built-in Methods, Dictionary Keys, Sorting and Looping, Nested Dictionaries.
- Dictionaries: Unordered collections of key-value pairs, defined using curly braces `{}` with keys and values separated by colons `:`. Keys must be unique and immutable.
my_dict = {"name": "Alice", "age": 30, "city": "New York"}
- Built-in Functions (for Dictionaries): `len()`, `str()` (string representation), `type()`.
print(len(my_dict)) # 3 print(str(my_dict)) # "{'name': 'Alice', 'age': 30, 'city': 'New York'}" print(type(my_dict)) # <class 'dict'>
- Built-in Methods (for Dictionaries):
- `keys()`: Returns a view object that displays a list of all the keys in the dictionary.
keys = my_dict.keys() # dict_keys(['name', 'age', 'city']) print(list(keys)) # ['name', 'age', 'city']
- `values()`: Returns a view object that displays a list of all the values in the dictionary.
values = my_dict.values() # dict_values(['Alice', 30, 'New York']) print(list(values)) # ['Alice', 30, 'New York']
- `items()`: Returns a view object that displays a list of all the key-value pairs (as tuples).
items = my_dict.items() # dict_items([('name', 'Alice'), ('age', 30), ('city', 'New York')]) print(list(items)) # [('name', 'Alice'), ('age', 30), ('city', 'New York')]
- `keys()`: Returns a view object that displays a list of all the keys in the dictionary.
- Important Dictionary Methods:
- `get(key, default)`: Returns the value for the specified key. If the key is not present, it returns the `default` value (or `None` if no default is provided).
name = my_dict.get("name") # "Alice" country = my_dict.get("country") # None country = my_dict.get("country", "Unknown") # "Unknown"
- `update(other_dict)`: Updates the dictionary with the key-value pairs from `other_dict`. Existing keys are overwritten, new keys are added.
more_data = {"country": "USA", "occupation": "Engineer"} my_dict.update(more_data) # my_dict is now {"name": "Alice", "age": 30, "city": "New York", "country": "USA", "occupation": "Engineer"}
- `pop(key, default)`: Removes the key and returns the corresponding value. If the key is not found, it returns the `default` value (or raises a `KeyError` if no default is provided).
age = my_dict.pop("age") # age is 30, my_dict is now {"name": "Alice", "city": "New York", "country": "USA", "occupation": "Engineer"} job = my_dict.pop("job", "Not found") # job is "Not found"
- `popitem()`: Removes and returns an arbitrary (key, value) pair from the dictionary.
item = my_dict.popitem()
- `clear()`: Removes all items from the dictionary.
my_dict.clear()
- `get(key, default)`: Returns the value for the specified key. If the key is not present, it returns the `default` value (or `None` if no default is provided).
- Dictionary Keys: Keys must be immutable (e.g., strings, numbers, tuples).
- Sorting and Looping:
- Iterating through a dictionary:
for key in my_dict: print(key, my_dict[key]) for key, value in my_dict.items(): print(key, value)
- Sorting a dictionary (returns a sorted list of keys):
sorted_keys = sorted(my_dict) # Sorts by keys sorted_items = sorted(my_dict.items(), key=lambda item: item[1]) # Sorts by values print(sorted_keys) print(sorted_items)
- Iterating through a dictionary:
- Nested Dictionaries: Dictionaries can contain other dictionaries as values, allowing you to represent hierarchical data structures.
employee = { "name": "Alice", "details": { "age": 30, "city": "New York", "office": { "building": "Empire State Building", "floor": 10 } } } print(employee["details"]["office"]["building"]) # "Empire State Building"
Files
- File Objects, File Built-in Function, File Built-in Methods, File Built-in Attributes, Standard Files, Command-line Arguments. File System. File Execution, Persistent Storage Modules.
- File Objects: Represent files in the operating system. You interact with files in Python through file objects.
- File Built-in Function: `open()`: Used to open a file. It takes the file path and the mode of opening the file as arguments (e.g., 'r' for read, 'w' for write, 'a' for append, 'b' for binary).
file = open("my_file.txt", "r") # Opens the file in read mode
- File Built-in Methods: Methods for reading from and writing to files.
- `read()`: Reads the entire file content as a string.
file_content = file.read()
- `readline()`: Reads a single line from the file.
line = file.readline()
- `readlines()`: Reads all lines from the file and returns them as a list of strings.
lines = file.readlines()
- `write(string)`: Writes a string to the file.
file.write("Hello, world!")
- `writelines(list_of_strings)`: Writes a list of strings to the file.
file.writelines(["Line 1\n", "Line 2\n"])
- `close()`: Closes the file. It's crucial to close files to free up system resources and ensure that any buffered data is written to the file. It's often used with a `try...finally` block or the `with` statement to ensure closure.
file.close()
try: file = open("my_file.txt", "r") # Do something with the file finally: file.close()
with open("my_file.txt", "r") as file: # Do something with the file # File is automatically closed after the 'with' block
- `seek(offset, whence)`: Changes the file's current position.
file.seek(0) # Go to the beginning of the file file.seek(10) # Go to the 10th byte
- `tell()`: Returns the file's current position.
position = file.tell() print(position)
- `read()`: Reads the entire file content as a string.
- File Built-in Attributes: Attributes of a file object.
- `name`: The name of the file.
print(file.name)
- `mode`: The mode in which the file was opened.
print(file.mode)
- `closed`: A Boolean indicating whether the file is closed.
print(file.closed)
- `name`: The name of the file.
- Standard Files:
- `sys.stdin`: The standard input stream (usually the keyboard).
- `sys.stdout`: The standard output stream (usually the console).
- `sys.stderr`: The standard error stream (used for displaying error messages).
- Command-line Arguments: Arguments passed to a Python script when it's executed from the command line. They are accessible through the `sys.argv` list. `sys.argv[0]` is the name of the script itself.
import sys print("Script name:", sys.argv[0]) for i, arg in enumerate(sys.argv[1:]): print(f"Argument {i+1}: {arg}") # If you run the script as: python my_script.py arg1 arg2 arg3 # Output: # Script name: my_script.py # Argument 1: arg1 # Argument 2: arg2 # Argument 3: arg3
- File System: Interacting with the file system using modules like `os` and `shutil`. Operations include:
- Creating and deleting directories (`os.mkdir()`, `os.rmdir()`, `os.makedirs()`, `os.removedirs()`).
import os os.mkdir("my_directory") os.makedirs("my_directory/sub_directory") # Creates nested directories os.rmdir("my_directory") # Removes an empty directory os.removedirs("my_directory/sub_directory") # Removes directory and all its sub-directories
- Checking file/directory existence (`os.path.exists()`, `os.path.isfile()`, `os.path.isdir()`).
import os.path print(os.path.exists("my_file.txt")) print(os.path.isfile("my_file.txt")) print(os.path.isdir("my_directory"))
- Renaming files/directories (`os.rename()`).
os.rename("old_name.txt", "new_name.txt")
- Copying files (`shutil.copy()`, `shutil.copy2()`, `shutil.copytree()`).
import shutil shutil.copy("file1.txt", "file2.txt") # Copy file1.txt to file2.txt shutil.copy2("file1.txt", "file3.txt") # Copy file1.txt to file3.txt with metadata shutil.copytree("my_directory", "my_directory_backup") # Copies entire directory tree
- Getting file/directory information (e.g., size, modification time: `os.path.getsize()`, `os.path.getmtime()`).
import os.path size = os.path.getsize("my_file.txt") mod_time = os.path.getmtime("my_file.txt") print(size) print(mod_time)
- Changing the current working directory (`os.chdir()`, `os.getcwd()`).
import os os.chdir("/path/to/my/directory") current_dir = os.getcwd() print(current_dir)
- Listing files and directories (`os.listdir()`, `os.scandir()`).
import os files = os.listdir() print(files) with os.scandir() as entries: for entry in entries: print(entry.name, entry.is_file(), entry.is_dir())
- Creating and deleting directories (`os.mkdir()`, `os.rmdir()`, `os.makedirs()`, `os.removedirs()`).
- File Execution: Running other programs or scripts from within a Python script using modules like `subprocess` or `os.system()`.
import subprocess subprocess.run(["ls", "-l"]) # Runs the 'ls -l' command # To capture the output: result = subprocess.run(["ls", "-l"], capture_output=True, text=True) print(result.stdout)
- Persistent Storage Modules: Modules for storing data persistently (i.e., the data remains even after the program terminates).
- `pickle`: Serializes Python objects into a byte stream, which can be stored in a file. Used for storing complex data structures.
import pickle data = {"name": "Alice", "age": 30} with open("my_data.pickle", "wb") as f: pickle.dump(data, f) with open("my_data.pickle", "rb") as f: loaded_data = pickle.load(f) print(loaded_data)
- `json`: Encodes and decodes data in JSON (JavaScript Object Notation) format, a lightweight and human-readable format. Useful for data exchange between applications.
import json data = {"name": "Alice", "age": 30} json_string = json.dumps(data) print(json_string) with open("my_data.json", "w") as f: json.dump(data, f) with open("my_data.json", "r") as f: loaded_data = json.load(f) print(loaded_data)
- `shelve`: Provides a dictionary-like interface for persistent storage of Python objects, using a database-backed storage.
import shelve with shelve.open("my_data.shelve") as db: db["name"] = "Alice" db["age"] = 30 with shelve.open("my_data.shelve") as db: print(db["name"]) print(db["age"])
- `pickle`: Serializes Python objects into a byte stream, which can be stored in a file. Used for storing complex data structures.
- Regular Expression: Introduction/Motivation , Special Symbols and Characters for REs , REs and Python.
- Regular Expressions (Regex): Powerful tools for pattern matching in strings. They provide a concise way to search, validate, and manipulate text based on specific patterns.
- Introduction/Motivation:
- Why use regular expressions?
- Searching: Find specific patterns within large amounts of text (e.g., finding all email addresses in a document).
- Validation: Verify that strings conform to a specific format (e.g., validating phone numbers, email addresses, URLs).
- Manipulation: Replace or extract parts of a string based on a pattern (e.g., replacing all occurrences of a word, extracting data from a log file).
- Why use regular expressions?
- Special Symbols and Characters for REs (Metacharacters):
- `.` (dot): Matches any character except a newline.
import re text = "abc\ndef" match = re.search(r".", text) if match: print(match.group(0)) # Output: a
- `^` (caret): Matches the beginning of a string.
text = "Start here" match = re.search(r"^Start", text) if match: print("Match at the beginning")
- `$` (dollar): Matches the end of a string.
text = "End here" match = re.search(r"here$", text) if match: print("Match at the end")
- `\*` (asterisk): Matches the preceding character zero or more times.
text = "abccc" match = re.search(r"bc*", text) if match: print(match.group(0)) # Output: bccc
- `+` (plus): Matches the preceding character one or more times.
text = "abccc" match = re.search(r"bc+", text) if match: print(match.group(0)) # Output: bccc
- `?` (question mark): Matches the preceding character zero or one time.
text = "abc" match = re.search(r"ab?c", text) if match: print(match.group(0)) # Output: abc text = "ac" match = re.search(r"ab?c", text) if match: print(match.group(0)) # Output: ac
- `[]` (square brackets): Defines a character class, matching any character within the brackets (e.g., `[a-z]` matches any lowercase letter).
text = "a1b2c3d4" match = re.findall(r"[a-c]", text) print(match) # Output: ['a', 'b', 'c']
- `[^]` (negated square brackets): Matches any character *not* within the brackets.
text = "a1b2c3d4" match = re.findall(r"[^0-9]", text) print(match) # Output: ['a', 'b', 'c', 'd']
- `\` (backslash): Escapes special characters (e.g., `\.` matches a literal dot, `\d` matches a digit).
text = "123.456" match = re.search(r"\.", text) if match: print(match.group(0)) # Output: . text = "abc123def" match = re.search(r"\d+", text) if match: print(match.group(0)) # Output: 123
- `|` (pipe): Specifies alternation (e.g., `a|b` matches either "a" or "b").
text = "apple or banana" match = re.findall(r"apple|banana", text) print(match) # Output: ['apple', 'banana']
- `()` (parentheses): Groups characters together and creates capturing groups (used for extracting matched portions of the string).
text = "name: John, age: 30" match = re.search(r"name: (\w+), age: (\d+)", text) if match: print(match.group(1)) # Output: John print(match.group(2)) # Output: 30
- `{m,n}` (curly braces): Matches the preceding character at least `m` times and at most `n` times.
text = "aab" match = re.search(r"a{2,3}b", text) if match: print(match.group(0)) # Output: aab text = "aaab" match = re.search(r"a{2,3}b", text) if match: print(match.group(0)) # Output: aaab text = "aaaaab" match = re.search(r"a{2,3}b", text) if match: print(match.group(0)) # Output: None
- `.` (dot): Matches any character except a newline.
- REs and Python: The `re` module in Python provides functions for working with regular expressions.
- `re.search(pattern, string)`: Searches for the first occurrence of the pattern in the string. Returns a match object if found, otherwise `None`.
import re text = "The quick brown fox" match = re.search(r"quick", text) if match: print("Found a match!") print(match.start(), match.end()) # 4, 9 else: print("No match")
- `re.match(pattern, string)`: Tries to match the pattern at the \*beginning\* of the string. Returns a match object if found, otherwise `None`.
import re text = "The quick brown fox" match = re.match(r"The", text) if match: print("Match at the beginning") else: print("No match at the beginning") match = re.match(r"quick", text) if match: print("Match at the beginning") else: print("No match at the beginning")
- `re.findall(pattern, string)`: Finds all occurrences of the pattern in the string and returns them as a list of strings.
import re text = "The quick brown fox jumps over the lazy fox" matches = re.findall(r"fox", text) print(matches) # Output: ['fox', 'fox']
- `re.finditer(pattern, string)`: Returns an iterator yielding match objects for all occurrences of the pattern.
import re text = "The quick brown fox jumps over the lazy fox" for match in re.finditer(r"fox", text): print(match.start(), match.end())
- `re.sub(pattern, replacement, string)`: Replaces occurrences of the pattern in the string with the `replacement` string.
import re text = "The quick brown fox" new_text = re.sub(r"fox", "dog", text) print(new_text) # Output: The quick brown dog
- `re.split(pattern, string)`: Splits the string into a list of substrings, using the pattern as the delimiter.
import re text = "apple,banana,cherry" result = re.split(r",", text) print(result) # Output: ['apple', 'banana', 'cherry']
- `re.compile(pattern)`: Compiles the pattern into a regex object, which can be used for multiple matching operations, improving efficiency.
import re pattern = re.compile(r"\d+") text1 = "abc123def456" text2 = "ghi789jkl" match1 = pattern.search(text1) match2 = pattern.search(text2) print(match1.group(0)) # 123 print(match2.group(0)) # 789
- `re.search(pattern, string)`: Searches for the first occurrence of the pattern in the string. Returns a match object if found, otherwise `None`.
UNIT-V
- Database Interaction: SQL Database Connection using Python, Creating and Searching Tables, Reading and storing config information on database, Programming using database connections.
- SQL Database Connection using Python: Python can connect to various relational database management systems (RDBMS) using database connectors. Examples:
- `sqlite3`: For working with SQLite databases (a lightweight, file-based database). Included in the Python standard library.
import sqlite3 conn = sqlite3.connect("my_database.db") cursor = conn.cursor()
- `psycopg2`: For PostgreSQL.
import psycopg2 conn = psycopg2.connect( host="localhost", database="my_database", user="my_user", password="my_password") cursor = conn.cursor()
- `mysql-connector-python`: For MySQL.
import mysql.connector conn = mysql.connector.connect( host="localhost", user="my_user", password="my_password", database="my_database") cursor = conn.cursor()
- `pyodbc`: For ODBC connections (can connect to various databases).
import pyodbc conn = pyodbc.connect( "Driver={SQL Server};" "Server=my_server;" "Database=my_database;" "UID=my_user;" "PWD=my_password;") cursor = conn.cursor()
- `sqlite3`: For working with SQLite databases (a lightweight, file-based database). Included in the Python standard library.
- Creating and Searching Tables: Using SQL statements (e.g., `CREATE TABLE`, `SELECT`, `INSERT`, `UPDATE`, `DELETE`) within Python code to interact with the database. This involves:
- Establishing a connection to the database.
- Creating a cursor object, which allows you to execute SQL queries.
- Executing SQL queries using the cursor's methods (e.g., `execute()`, `executemany()`).
# Create table cursor.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT, age INTEGER ) """) # Insert data cursor.execute("INSERT INTO users (name, age) VALUES (?, ?)", ("Alice", 30)) cursor.executemany("INSERT INTO users (name, age) VALUES (?, ?)", [("Bob", 25), ("Charlie", 35)]) # Select data cursor.execute("SELECT * FROM users") rows = cursor.fetchall() for row in rows: print(row) # Update data cursor.execute("UPDATE users SET age = ? WHERE name = ?", (31, "Alice")) # Delete data cursor.execute("DELETE FROM users WHERE name = ?", ("Charlie",)) conn.commit()
- Fetching data from the result set (e.g., `fetchone()`, `fetchall()`).
- Committing changes to the database.
- Closing the cursor and the connection.
conn.close()
- Reading and storing config information on database: Databases can be used to store configuration settings for applications, instead of using flat files. This allows for easier management, querying, and updating of configuration data.
- Programming using database connections: Building applications that interact with databases to store and retrieve data. This is a fundamental aspect of many software applications, including web applications, data-driven applications, and enterprise systems.
- Python Multithreading: Understanding threads, Forking threads, synchronizing the threads, Programming using multithreading.
- Understanding threads:
- A thread is a lightweight unit of execution within a process. A process can have multiple threads running concurrently.
- Multithreading allows a program to perform multiple tasks seemingly simultaneously, improving responsiveness and efficiency, especially for I/O-bound operations.
- The Global Interpreter Lock (GIL) in CPython (the standard Python implementation) limits true parallelism for CPU-bound tasks. Only one thread can hold the GIL at a time. However, multithreading can still be beneficial for I/O-bound tasks, as the GIL is released when a thread is waiting for I/O.
- Forking threads: Creating new threads using the `threading` module.
import threading def my_function(arg1, arg2): print(f"Thread started with args: {arg1}, {arg2}") # Perform some task thread = threading.Thread(target=my_function, args=("hello", 123)) thread.start() thread.join() # Wait for the thread to finish
- Synchronizing the threads: Ensuring that multiple threads can access shared resources without causing data corruption or race conditions. Techniques include:
- Locks (`threading.Lock`, `threading.RLock`): A lock is a synchronization primitive that allows only one thread to acquire it at a time. Threads must acquire the lock before accessing shared resources and release it when they are done. RLock (reentrant lock) allows a thread to acquire the same lock multiple times.
import threading lock = threading.Lock() def my_function(shared_resource): lock.acquire() try: # Access and modify the shared resource shared_resource += 1 finally: lock.release()
- Semaphores (`threading.Semaphore`): A semaphore manages a counter that represents the number of available resources. Threads can acquire a resource (decrement the counter) or release a resource (increment the counter).
import threading semaphore = threading.Semaphore(3) # Allow 3 threads to access concurrently def my_function(resource): semaphore.acquire() try: # Use the resource print(f"Thread using resource: {resource}") finally: semaphore.release()
- Conditions (`threading.Condition`): Allows threads to wait for a specific condition to become true. It's used with a lock.
import threading condition = threading.Condition() shared_resource = 0 def producer(): global shared_resource condition.acquire() shared_resource = 1 condition.notify() # Notify waiting consumer condition.release() def consumer(): global shared_resource condition.acquire() while shared_resource == 0: condition.wait() # Wait for producer to notify print(f"Consumer got resource: {shared_resource}") condition.release()
- Events (`threading.Event`): A simple synchronization object that can be set to `True` (signaled) or `False`. Threads can wait for the event to be set.
import threading event = threading.Event() def worker(): event.wait() # Wait until the event is set print("Worker received the signal") def master(): # Perform some setup event.set() # Signal the worker thread
- Queues (`queue.Queue`, `queue.LifoQueue`, `queue.PriorityQueue`): Thread-safe data structures that can be used for communication between threads.
import queue q = queue.Queue() def producer(): for i in range(5): q.put(i) # Put items in the queue def consumer(): while not q.empty(): item = q.get() # Get items from the queue print(f"Consumer got: {item}")
- Locks (`threading.Lock`, `threading.RLock`): A lock is a synchronization primitive that allows only one thread to acquire it at a time. Threads must acquire the lock before accessing shared resources and release it when they are done. RLock (reentrant lock) allows a thread to acquire the same lock multiple times.
- Programming using multithreading: Designing and implementing applications that use multiple threads to improve performance or handle concurrent tasks. Careful consideration is needed to avoid common multithreading problems like:
- Race conditions: When the outcome of a program depends on the unpredictable order of execution of multiple threads.
- Deadlocks: When two or more threads are blocked indefinitely, waiting for each other to release resources.
- Starvation: When a thread is repeatedly denied access to a resource and cannot make progress.
</html>