Python Viva Notes
Python Viva Notes
Python supports both object-oriented and procedural programming paradigms, making it versatile. In object-oriented programming, Python allows encapsulation, inheritance, and polymorphism through classes and objects, which helps organize code into reusable components. Procedural programming allows for a straightforward execution of tasks through functions and statements, which can be easier for simple scripting and achieving quick tasks. This dual support enables developers to choose their preferred programming style and leverage Python's full capabilities, adapting to different scenarios and needs .
Decorators in Python are functions that modify another function's behavior. They are applied by prefixing a function definition with '@decorator_name'. For example, a simple log decorator could be defined as 'def log_decorator(func): def wrapper(*args, **kwargs): print(f'Function {func.__name__} called') return func(*args, **kwargs) return wrapper'. When used as '@log_decorator', any call to the decorated function will first print the log message before executing the function, thereby modifying its behavior without changing the original code .
Python's Global Interpreter Lock (GIL) is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecode concurrently in a multi-threaded program. This means that even on multi-core systems, only one thread can execute Python code at a time, effectively limiting the benefits of multithreading for CPU-bound tasks. Potential solutions include using multiprocessing, which runs separate processes with their own Python interpreter and memory space, or employing C extensions to handle performance-critical parts where threading is necessary for concurrency without GIL interference .
An ORM like SQLAlchemy in Python simplifies database interaction by allowing developers to interact with the database using Python objects and classes, rather than SQL queries. This abstraction layer translates Python operations into SQL commands, facilitating code that is more modular and easier to maintain. However, this abstraction can potentially lead to performance overhead if not optimized, as well as limited flexibility in executing complex queries compared to raw SQL. Understanding the underlying SQL being executed is crucial to mitigating these trade-offs .
Using 'with open(...) as f:' is preferred because it ensures that files are properly closed after their suite finishes, even if an exception is raised within the block. This context manager automatically handles resource management, promoting cleaner and more robust code without manual closing calls. The traditional method requires explicit calls to 'close()', leading to potential issues if an error occurs before the file is closed. This programming practice thus prevents resource leaks and simplifies exception handling .
The '==' operator in Python checks for value equality, meaning it compares the contents of two objects. The 'is' keyword, however, checks for identity equality, meaning it determines if two references point to the same memory location. For instance, 'x == y' will return True if x and y have the same value, but 'x is y' will return True only if both are actually the same object in memory. An example usage of 'is' would be checking if a variable is None, e.g., 'if x is None:' .
Python's interpreted nature means the code is executed line by line at runtime without being converted to machine code first. This allows for quicker testing and debugging as changes can be tested immediately without the need for recompilation. However, it generally results in slower execution times compared to compiled languages like C or C++, as there is an overhead for translating code on the fly .
Lists in Python are ordered collections that allow duplicate elements and are mutable, allowing for item insertion and deletion, which is ideal for maintaining a sequence of elements where ordering and duplicates matter. Sets, on the other hand, are unordered collections that do not allow duplicates, making them suitable for operations like union, intersection, and difference where uniqueness is required. Sets are particularly advantageous in scenarios involving data deduplication and mathematical set operations due to their optimized handling of unique elements .
The '__init__' method in Python is a special method used as a constructor to initialize a newly created object's state. It's not mandatory, but it allows developers to set initial values or execute startup operations when an object is instantiated. Its role is crucial in object-oriented programming as it enables the creation of objects with specific attributes. Example: 'class Car: def __init__(self, brand, model): self.brand = brand self.model = model' creates a Car object with brand and model attributes initialized during object creation .
Generators in Python are functions that use the 'yield' statement to return results one at a time, pausing their execution between yields. This allows them to maintain their state and resume where they left off, providing items one at a time rather than all at once like traditional functions. They are memory efficient because they do not require all the generated values to be stored in memory simultaneously, making them suitable for iterating over large data sets without consuming excessive memory resources .