What are the built-in exception classes in Python?

Python provides a comprehensive set of built-in exception classes for handling various error conditions. These exceptions are derived from the base class BaseException. Below is a categorized list of the most common built-in exceptions in Python:


1. Base Classes

  • BaseException: The base class for all built-in exceptions. You usually do not handle this directly.
  • Exception: The base class for all exceptions that are not system-exiting. Most user-defined exceptions inherit from this.

2. SystemExit and Environment Exceptions

  • SystemExit: Raised when sys.exit() is called to exit the program.
  • KeyboardInterrupt: Raised when the user interrupts program execution (e.g., pressing Ctrl+C).
  • GeneratorExit: Raised when a generator’s close() method is called.

3. Standard Exceptions

Errors Related to Syntax and Logic:

  • SyntaxError: Raised when the parser encounters a syntax error.
  • IndentationError: Raised when there is incorrect indentation.
    • TabError: Raised when indentation consists of inconsistent tabs and spaces.

Errors Related to Value, Type, and Index:

  • TypeError: Raised when an operation is performed on an inappropriate type.
  • ValueError: Raised when a function receives an argument of the correct type but inappropriate value.
  • IndexError: Raised when a sequence index is out of range.
  • KeyError: Raised when a key is not found in a dictionary.

Errors Related to Numbers:

  • ZeroDivisionError: Raised when attempting to divide by zero.
  • OverflowError: Raised when a mathematical operation exceeds the limits of the data type.
  • FloatingPointError: Raised for floating-point operations (rare).

Errors Related to Iteration:

  • StopIteration: Raised by an iterator to signal that there are no further items.
  • StopAsyncIteration: Raised by asynchronous iterators to signal the end of iteration.

Errors Related to Importing Modules:

  • ImportError: Raised when an import statement fails.
    • ModuleNotFoundError: A subclass of ImportError, raised when a module cannot be found.

Errors Related to File and Input/Output:

  • FileNotFoundError: Raised when a file or directory is requested but not found.
  • PermissionError: Raised when an operation lacks the necessary permissions.
  • IOError: Raised for I/O operations (e.g., file not found, disk full).
  • EOFError: Raised when the input() function hits end-of-file.

Errors Related to Lookups and Attribute Access:

  • AttributeError: Raised when an invalid attribute is accessed.
  • NameError: Raised when a local or global name is not found.
    • UnboundLocalError: A subclass of NameError, raised for references to uninitialized local variables.

Errors Related to Assertions and Arithmetic:

  • AssertionError: Raised when an assert statement fails.
  • ArithmeticError: The base class for all arithmetic errors.
    • FloatingPointError
    • OverflowError
    • ZeroDivisionError

4. Warnings

Warnings are not exceptions but are used to notify developers of potential issues.

  • Warning: The base class for all warnings.
    • UserWarning
    • DeprecationWarning
    • SyntaxWarning
    • RuntimeWarning
    • FutureWarning

5. OS-Related Exceptions

  • OSError: Raised for system-related errors (e.g., file not found, disk full).
    • FileExistsError: Raised when a file or directory already exists.
    • IsADirectoryError: Raised when a directory operation is expected but a file is given.
    • NotADirectoryError: Raised when a file operation is expected but a directory is given.
    • PermissionError
    • FileNotFoundError

6. Specific Runtime and Execution Errors

  • RuntimeError: Raised when an error doesn’t fit into any other category.
  • NotImplementedError: Raised when an abstract method needs to be overridden by a subclass.
  • RecursionError: Raised when the maximum recursion depth is exceeded.

7. Memory-Related Errors

  • MemoryError: Raised when an operation runs out of memory.
  • BufferError: Raised when performing an operation on a buffer that the operation cannot handle.

8. Connection and Network Errors

  • ConnectionError: The base class for network-related exceptions.
    • ConnectionAbortedError
    • ConnectionRefusedError
    • ConnectionResetError
  • TimeoutError: Raised when a connection times out.

9. Custom and User-Defined Exceptions

Python allows developers to create custom exceptions by subclassing Exception.

Example:

class CustomError(Exception):
    pass

try:
    raise CustomError("Something went wrong!")
except CustomError as e:
    print(e)

Hierarchy of Built-In Exceptions

All exceptions are part of the following hierarchy:

BaseException
 ├── SystemExit
 ├── KeyboardInterrupt
 ├── GeneratorExit
 └── Exception
      ├── ArithmeticError
      ├── AttributeError
      ├── EOFError
      ├── ImportError
      ├── IndexError
      ├── KeyError
      ├── NameError
      ├── OSError
      ├── RuntimeError
      ├── TypeError
      ├── ValueError
      ├── ...

These exceptions cover a wide range of scenarios, helping developers handle errors effectively in Python programs.

How would you implement a queue or a stack in Python ?

In Python, you can implement a queue or a stack using various data structures, such as lists or the collections.deque module. Below is how you can implement both:

1. Stack Implementation

A stack follows the “Last In, First Out” (LIFO) principle, meaning the last element added is the first one to be removed.

Using a List

class Stack:
    def __init__(self):
        self.stack = []

    def push(self, item):
        self.stack.append(item)  # Adds an item to the end of the list

    def pop(self):
        if not self.is_empty():
            return self.stack.pop()  # Removes and returns the last item
        return None

    def peek(self):
        if not self.is_empty():
            return self.stack[-1]  # Returns the last item without removing it
        return None

    def is_empty(self):
        return len(self.stack) == 0  # Checks if the stack is empty

    def size(self):
        return len(self.stack)  # Returns the number of items in the stack

Example usage:

stack = Stack()
stack.push(10)
stack.push(20)
print(stack.pop())  # Output: 20
print(stack.peek())  # Output: 10

2. Queue Implementation

A queue follows the “First In, First Out” (FIFO) principle, meaning the first element added is the first one to be removed.

Using a List

You can use a list, but removing items from the front can be inefficient (O(n) time complexity). To implement an efficient queue, it’s better to use collections.deque.

Using collections.deque

from collections import deque

class Queue:
    def __init__(self):
        self.queue = deque()

    def enqueue(self, item):
        self.queue.append(item)  # Adds an item to the end of the deque

    def dequeue(self):
        if not self.is_empty():
            return self.queue.popleft()  # Removes and returns the first item
        return None

    def peek(self):
        if not self.is_empty():
            return self.queue[0]  # Returns the first item without removing it
        return None

    def is_empty(self):
        return len(self.queue) == 0  # Checks if the queue is empty

    def size(self):
        return len(self.queue)  # Returns the number of items in the queue

Example usage:

queue = Queue()
queue.enqueue(10)
queue.enqueue(20)
print(queue.dequeue())  # Output: 10
print(queue.peek())  # Output: 20

Summary

  • Stack: Can be implemented with a list where items are added with append() and removed with pop().
  • Queue: Can be implemented using deque for efficient appending and popping from both ends.

Both of these implementations support the basic operations like adding, removing, and checking the status of the queue or stack.

What are Python’s built-in data types?

Understanding Python’s Built-in Data Types

Python is a versatile and powerful programming language that offers a variety of built-in data types to manage different kinds of data efficiently. Whether you’re a beginner or an experienced developer, understanding these data types is essential for writing clean and effective Python code. Let’s dive into Python’s built-in data types and explore how they work.


1. Numeric Types

Numeric types are used to store numbers, including integers, floating-point numbers, and complex numbers.

  • int: Represents whole numbers (e.g., 1, -2, 100). x = 10 # An integer value
  • float: Represents decimal or floating-point numbers (e.g., 3.14, -0.001). y = 3.14 # A floating-point value
  • complex: Represents complex numbers with real and imaginary parts (e.g., 1+2j). z = 1 + 2j # A complex number

2. Sequence Types

Sequence types are used to store collections of items in a specific order.

  • list: An ordered, mutable collection of items. Examples: my_list = [1, 2, 3, "hello"]
  • tuple: An ordered, immutable collection of items. Examples: my_tuple = (1, 2, 3, "world")
  • range: Represents a sequence of numbers, typically used for loops. Examples: r = range(5) # Represents numbers 0 to 4

3. Text Type

Python’s text type is used to handle and manipulate textual data.

  • str: Represents an immutable sequence of Unicode characters. Examples: text = "Hello, Python!"

4. Set Types

Set types store collections of unique items, ensuring no duplicates.

  • set: An unordered collection of unique items. Examples: my_set = {1, 2, 3, 4}
  • frozenset: An immutable version of a set. Examples: my_frozenset = frozenset([1, 2, 3, 4])

5. Mapping Type

Mapping types store key-value pairs for quick data retrieval.

  • dict: A mutable and unordered collection of key-value pairs. Examples: my_dict = {"name": "Alice", "age": 25}

6. Boolean Type

Boolean types represent truth values.

  • bool: Can only have two values: True or False. Examples: is_active = True

7. Binary Types

Binary types manage binary data, such as bytes.

  • bytes: An immutable sequence of bytes. Examples: b = b"hello"
  • bytearray: A mutable sequence of bytes. Examples: ba = bytearray(b"hello")
  • memoryview: Provides access to the memory of a binary object without copying it. Examples: mv = memoryview(b"hello")

8. None Type

The NoneType is used to represent the absence of a value.

  • None: Indicates a null or undefined value. Examples: x = None

Dynamic Typing in Python

Python is a dynamically typed language, meaning variables don’t need explicit type declarations. The type is determined at runtime based on the assigned value. You can check the type of any variable using the type() function:

x = 42
print(type(x))  # Output: <class 'int'>

Conclusion

Python’s built-in data types provide a robust foundation for managing different types of data in your programs. From numbers to collections and text, these data types make Python a highly flexible language. Whether you’re building a simple script or a complex application, mastering these data types is key to writing efficient and effective code.

Got questions? Drop a comment below and let’s discuss! If you found this post helpful, don’t forget to share it with your fellow Python enthusiasts.

How does Python handle mutable and immutable data types?

In Python, the distinction between mutable and immutable data types is fundamental to how the language handles memory and variable assignments. Here’s a breakdown:


Mutable Data Types

Mutable data types allow modifications after their creation. You can change their content, such as adding, removing, or updating elements, without creating a new object.

Examples of Mutable Data Types

  1. Lists: my_list = [1, 2, 3] my_list.append(4) # Modifies the original list print(my_list) # Output: [1, 2, 3, 4]
  2. Dictionaries: my_dict = {"key1": "value1", "key2": "value2"} my_dict["key3"] = "value3" # Adds a new key-value pair print(my_dict) # Output: {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
  3. Sets: my_set = {1, 2, 3} my_set.add(4) # Modifies the original set print(my_set) # Output: {1, 2, 3, 4}

Key Characteristics of Mutables:

  • Changes affect the original object.
  • Mutable objects can lead to unexpected side effects when shared across variables or passed as arguments.

Immutable Data Types

Immutable data types cannot be changed after creation. Any operation that tries to modify an immutable object creates a new object instead.

Examples of Immutable Data Types

  1. Strings: my_string = "hello" new_string = my_string + " world" # Creates a new string print(my_string) # Output: hello print(new_string) # Output: hello world
  2. Tuples: my_tuple = (1, 2, 3) # my_tuple[0] = 0 # This will raise a TypeError
  3. Numbers (integers, floats, etc.): num = 10 num += 5 # Creates a new integer object print(num) # Output: 15
  4. Frozensets: my_frozenset = frozenset([1, 2, 3]) # my_frozenset.add(4) # This will raise an AttributeError

Key Characteristics of Immutables:

  • Content cannot be changed once created.
  • Immutable objects are thread-safe and often used as dictionary keys or set elements because they guarantee hash consistency.

Behavior of Variables with Mutables and Immutables

  1. Mutables: Assigning a mutable object to another variable creates a reference to the same object, not a copy. list1 = [1, 2, 3] list2 = list1 # Both variables reference the same list list2.append(4) print(list1) # Output: [1, 2, 3, 4]
  2. Immutables: Assigning an immutable object to another variable creates a new object when modified. a = 10 b = a # Both reference the same value initially b += 5 # Creates a new integer object for b print(a) # Output: 10 print(b) # Output: 15

Why Does This Matter?

Understanding mutability helps in:

  • Avoiding unintended side effects when passing mutable objects to functions.
  • Writing efficient, thread-safe code.
  • Correctly managing memory and references in Python.