Top Python Interview Questions and Answers (2024) Part 2

Python

What is Scope in Python?

In Python, the term “scope” refers to the region of a program where a particular identifier, such as a variable or a function name, is accessible or visible. Python has four levels of scope:

  1. Local scope: Variables defined within a function are only accessible within that function. They are said to have a local scope.
  2. Enclosing (or non-local) scope: This refers to the scope of the enclosing function. If a function is defined inside another function, it can access variables from the enclosing function’s scope.
  3. Global scope: Variables defined at the top level of a module are accessible throughout the module. They are said to have global scope.
  4. Built-in scope: This is the widest scope and includes all the names that are built into Python, such as print(), len(), etc.

Python follows the LEGB rule to determine the order of scopes to look up names:

  • L: Local scope
  • E: Enclosing scope
  • G: Global scope
  • B: Built-in scope

Python searches for a name in these scopes in the order specified by the LEGB rule until it finds the name or reaches the built-in scope. If the name is not found in any of these scopes, Python raises a NameError.

What are lists and tuples?

Lists and tuples are both data structures in Python used to store collections of items, but they have some differences in their behavior and usage:

Lists:

  • Lists are ordered collections of items.
  • They are mutable, meaning their elements can be changed after creation.
  • Lists are defined using square brackets [ ].
  • Elements in a list can be accessed using indexing.
  • Lists can contain elements of different data types.
  • Lists are typically used when you have a collection of items that may need to be modified, such as a list of tasks, a list of numbers, etc.

Example of a list:

my_list = [1, 2, 3, 'apple', 'banana', 'orange']

Tuples:

  • Tuples are ordered collections of items, similar to lists.
  • They are immutable, meaning once a tuple is created, its elements cannot be changed.
  • Tuples are defined using parentheses ( ).
  • Elements in a tuple can be accessed using indexing.
  • Tuples can contain elements of different data types.
  • Tuples are typically used when you have a collection of items that should not be changed, such as coordinates, configuration settings, etc.

Example of a tuple:

my_tuple = (1, 2, 3, 'apple', 'banana', 'orange')

In summary, lists are mutable and defined with square brackets, while tuples are immutable and defined with parentheses. Both can contain elements of different data types and are used for storing collections of items, but the choice between them depends on whether mutability is desired and the specific requirements of the program.

What is pass in Python?

In Python, pass is a keyword that is used as a placeholder where syntactically some code is required, but you want to do nothing or indicate that the code will be added later.

It’s essentially a no-operation statement. When Python encounters pass, it does nothing and moves on to the next line of code. It’s often used as a placeholder when a statement is syntactically necessary but the programmer doesn’t want to perform any action.

Here’s an example of how pass can be used:

if condition:
    # code to be added later
    pass
else:
    # some other code
    print("Condition is False")

In this example, if the condition is true, the if block would be executed, but since there’s no actual code to execute yet, pass is used as a placeholder. Similarly, you might use pass in defining a function, class, or loop when you plan to implement its body later.

def my_function():
    # code to be added later
    pass

This can be useful during development when you’re sketching out the structure of your program and want to indicate where certain functionality will be added later.

What are modules and packages in Python?

In Python, a module is a file containing Python code. The code within a module can consist of functions, classes, or variables. Modules allow you to organize your Python code logically into separate files, making it easier to manage and maintain your codebase. You can import modules into other Python scripts or modules to reuse their functionality.

Here’s an example of a simple module:

# module.py
def greet(name):
    print("Hello, " + name + "!")

You can then import this module into another Python script and use its functionality:

# main.py
import module

module.greet("Alice")

Output:

Hello, Alice!

A package in Python is a collection of related modules organized within a directory. The directory containing the modules must include a special file called __init__.py. Packages allow you to further organize your code by grouping related functionality together.

Here’s an example of a package structure:

my_package/
    __init__.py
    module1.py
    module2.py

The __init__.py file can be empty, but it’s required to indicate that the directory is a Python package. You can then import modules from the package using dot notation:

# main.py
import my_package.module1

my_package.module1.some_function()

Alternatively, you can use relative imports within the package:

# module2.py
from . import module1

module1.some_function()

Packages allow for better organization and modularization of your codebase, making it easier to manage larger projects. They also facilitate code reuse and sharing across different projects.

What are global, protected and private attributes in Python?

In Python, attributes are variables associated with a class instance or a class itself. They can be classified into different access levels:

  1. Public attributes: These attributes can be accessed from outside the class. They are not prefixed with any underscore. By default, all attributes in Python are considered public unless explicitly marked otherwise.

Example:

class MyClass:
    def __init__(self):
        self.public_attribute = 10

obj = MyClass()
print(obj.public_attribute)  # Output: 10
  1. Protected attributes: Protected attributes are conventionally indicated by prefixing an underscore (_) to the attribute name. These attributes are intended to be accessed within the class itself and by its subclasses. While it’s not enforced by the language, it serves as a signal to other programmers that the attribute should be treated as protected.

Example:

class MyClass:
    def __init__(self):
        self._protected_attribute = 20

obj = MyClass()
print(obj._protected_attribute)  # Output: 20
  1. Private attributes: Private attributes are indicated by prefixing a double underscore (__) to the attribute name. This causes name mangling to occur, making it harder to access the attribute from outside the class. Private attributes are intended to be accessed only within the class that defines them.

Example:

class MyClass:
    def __init__(self):
        self.__private_attribute = 30

obj = MyClass()
# This will raise an AttributeError because the private attribute is not directly accessible
print(obj.__private_attribute)

However, Python does not have true private access control like some other languages (e.g., Java). You can still access private attributes using name mangling:

print(obj._MyClass__private_attribute)  # Output: 30

It’s important to note that while Python provides these conventions for indicating access levels, they are primarily for code readability and should be followed by convention rather than enforced by the language itself.

What is the use of self in Python?

In Python, self is a conventionally used parameter name that refers to the current instance of a class. It’s the first parameter in the definition of any instance method within a class. When you call a method on an instance of a class, Python automatically passes the instance itself as the first argument to the method. By convention, this parameter is named self, but you can name it whatever you like (though it’s highly recommended to stick to the convention for readability).

The primary purpose of self is to access and modify the attributes and methods of the current instance within the class definition. By using self, you can differentiate between instance variables (attributes) and local variables within methods.

Here’s a simple example to illustrate the use of self:

class MyClass:
    def __init__(self, value):
        self.value = value  # Define an instance variable 'value'

    def display_value(self):
        print(self.value)   # Access instance variable 'value' using 'self'

# Create an instance of MyClass
obj = MyClass(42)

# Call the instance method 'display_value' on the object
obj.display_value()  # Output: 42

In this example, self.value refers to the value attribute of the current instance of MyClass.

Without self, Python wouldn’t know which instance’s attribute or method you’re referring to within the class definition. By using self, you make it clear that you’re accessing or modifying the attributes and methods of the current instance.

What is init?

__init__ is a special method in Python classes that is automatically called when a new instance of the class is created. It is also known as the constructor method. The __init__ method is used to initialize the attributes (variables) of the class instance.

Here’s a basic example:

class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# Create an instance of MyClass and pass values for 'x' and 'y'
obj = MyClass(3, 5)

# Access the attributes initialized in the __init__ method
print(obj.x)  # Output: 3
print(obj.y)  # Output: 5

In this example, the __init__ method takes three parameters: self, x, and y. self represents the instance of the class itself, and x and y are the values used to initialize the attributes self.x and self.y, respectively.

When you create a new instance of the class MyClass and pass arguments to it (MyClass(3, 5)), Python automatically calls the __init__ method with the instance being created (obj) as the first argument (self). The values 3 and 5 are then assigned to the attributes x and y, respectively.

The __init__ method is commonly used to set up the initial state of an object by initializing its attributes. However, it’s not required to define an __init__ method in a class. If you don’t define one, Python provides a default __init__ method that does nothing.

What is break, continue and pass in Python?

In Python, break, continue, and pass are control flow statements used within loops and conditional statements to alter the flow of execution.

  1. break:
  • The break statement is used to terminate the current loop prematurely.
  • When Python encounters a break statement within a loop (such as for or while), it immediately exits the loop, skipping any remaining iterations.
  • break is commonly used to exit a loop early when a certain condition is met.

Example:

for i in range(5):
    if i == 3:
        break
    print(i)

Output:

0
1
2
  1. continue:
  • The continue statement is used to skip the rest of the code inside a loop for the current iteration and continue with the next iteration.
  • When Python encounters a continue statement within a loop, it skips the rest of the loop’s body for the current iteration and proceeds to the next iteration.
  • continue is commonly used to skip certain iterations based on a condition, without terminating the loop entirely.

Example:

for i in range(5):
    if i == 2:
        continue
    print(i)

Output:

0
1
3
4
  1. pass:
  • The pass statement is a no-operation statement that is used when a statement is syntactically required but you don’t want to execute any code.
  • It’s often used as a placeholder when defining a block of code that will be implemented later.
  • pass essentially does nothing and allows the code to continue executing without raising any errors.

Example:

if condition:
    # code to be added later
    pass
else:
    print("Condition is False")

In summary, break is used to exit a loop prematurely, continue is used to skip the rest of the loop for the current iteration, and pass is used as a placeholder when no action is needed.

What are unit tests in Python?

Unit tests in Python are a way to verify that individual units of code (such as functions, methods, or classes) work correctly in isolation. The primary goal of unit testing is to validate that each unit of code behaves as expected under various inputs and conditions.

Python provides a built-in module called unittest for writing and running unit tests. With unittest, you can create test cases that define the expected behavior of your code and check whether the actual output matches the expected output.

Here’s a basic example of a unit test using unittest:

import unittest

def add(a, b):
    return a + b

class TestAddFunction(unittest.TestCase):
    def test_add_positive_numbers(self):
        self.assertEqual(add(2, 3), 5)

    def test_add_negative_numbers(self):
        self.assertEqual(add(-2, -3), -5)

    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)

if __name__ == '__main__':
    unittest.main()

In this example:

  • We define a function add(a, b) that simply adds two numbers.
  • We create a test case class TestAddFunction that inherits from unittest.TestCase.
  • Within the test case class, we define individual test methods, each starting with the prefix test_.
  • Inside each test method, we use assertion methods provided by unittest.TestCase (such as assertEqual) to check whether the actual output of the function matches the expected output.

To run the tests, you typically execute the script containing the tests. When run, unittest automatically discovers test cases within the script and executes them, reporting any failures or errors.

Unit testing helps ensure that changes to your code don’t introduce unintended bugs or regressions. By writing tests for each unit of code, you can have confidence that your code behaves correctly as you make modifications and enhancements.

What is docstring in Python?

In Python, a docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Its primary purpose is to document the purpose, usage, and behavior of the code it accompanies. Docstrings are used to provide documentation that can be accessed using the built-in help() function or documentation generation tools like Sphinx.

Docstrings can be written using triple quotes (''' or """) and can span multiple lines. There are two common styles for writing docstrings: the one-line summary style and the multi-line style.

  1. One-line summary style:
  • This style consists of a single-line description of the function or method’s purpose.
  • It’s typically used for simple functions or methods with straightforward functionality.

Example:

def add(a, b):
    """Return the sum of two numbers."""
    return a + b
  1. Multi-line style:
  • This style includes a more detailed description of the function or method, along with information about its parameters, return value, and any exceptions it may raise.
  • It’s used for more complex functions or methods that require detailed documentation.

Example:

def divide(dividend, divisor):
    """
    Divide two numbers.

    Parameters:
    dividend (int): The number to be divided.
    divisor (int): The number by which the dividend is divided.

    Returns:
    float: The quotient of the division.

    Raises:
    ZeroDivisionError: If the divisor is zero.
    """
    if divisor == 0:
        raise ZeroDivisionError("Cannot divide by zero")
    return dividend / divisor

Docstrings are not just comments; they are accessible programmatically and are used by tools and libraries to generate documentation. Writing descriptive and informative docstrings can greatly improve the usability and maintainability of your code.

You May Also Read:

Top Python Interview Questions and Answers (2024) Part 1

Leave a Reply

Your email address will not be published. Required fields are marked *