Top Python Interview Questions and Answers (Latest 2024)
Try Aihirely for
Smarter Interview Prep
Experience real-time AI support tailored to your Resume.
Boost your confidence and ace every question with
AI Mock Interview.
Question: What is the concept of decorators in Python?
Answer:
In Python, a decorator is a special type of function that is used to modify or enhance the behavior of other functions or methods. Decorators allow you to wrap a function with additional functionality without modifying its actual code. This concept is used to add functionality to existing code in a modular and reusable way.
How Decorators Work:
A decorator is essentially a function that takes another function as input and returns a new function that usually extends or alters the behavior of the original function.
Decorator Syntax:
@decorator_function
def some_function():
## Function body
In this syntax:
- The
@decorator_functionis the decorator. - The
some_function()is the function being decorated.
This is equivalent to:
def some_function():
## Function body
some_function = decorator_function(some_function)
Basic Example of a Decorator:
Here’s an example of a simple decorator that prints a message before and after the execution of a function:
## Define the decorator function
def my_decorator(func):
def wrapper():
print("Before the function call.")
func()
print("After the function call.")
return wrapper
## Use the decorator with the @ symbol
@my_decorator
def say_hello():
print("Hello!")
## Call the decorated function
say_hello()
Output:
Before the function call.
Hello!
After the function call.
In this example:
my_decoratoris the decorator function.say_hellois the function being decorated.- When
say_hellois called, it is actually wrapped by thewrapperfunction inmy_decorator, which adds additional behavior.
Key Concepts of Decorators:
-
Functions are First-Class Objects: Functions in Python are first-class objects, which means they can be passed as arguments to other functions, returned as values, and assigned to variables. This is what allows decorators to work.
-
Higher-Order Functions: A decorator is a higher-order function, meaning it takes another function as an argument and returns a new function.
-
Closure: In the decorator example, the inner function (
wrapper) is a closure. It has access to the outer function’s variables, in this case, thefuncargument. This is why the decorator can modify or call the original function within thewrapper.
Decorators with Arguments:
Decorators can also be created to accept arguments if needed. To do this, you would define a decorator function that returns a function which can accept arguments.
Here’s an example of a decorator that takes an argument:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Hello, Alice!
Hello, Alice!
Hello, Alice!
In this example:
repeatis a decorator that takes an argumentn, which specifies how many times to repeat the decorated function call.- The
greetfunction is wrapped inwrapper, and it is calledntimes.
Built-In Decorators in Python:
Python has several built-in decorators that you can use, such as:
@staticmethod: Defines a static method in a class.@classmethod: Defines a class method.@property: Defines a property in a class (for getter/setter methods).
Example of using @staticmethod:
class MyClass:
@staticmethod
def greet():
print("Hello from a static method!")
MyClass.greet() # Output: Hello from a static method!
Decorators with Functions That Take Arguments:
If the decorated function takes arguments, you can use *args and **kwargs to ensure that all arguments are passed correctly:
def decorator(func):
def wrapper(*args, **kwargs):
print(f"Arguments passed: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@decorator
def add(a, b):
return a + b
print(add(2, 3)) # Output: Arguments passed: (2, 3), {}
## 5
In this example:
- The
wrapperfunction prints the arguments passed to the decoratedaddfunction.
Chaining Decorators:
You can apply multiple decorators to a single function. When this happens, the decorators are applied in a bottom-to-top order, i.e., the decorator closest to the function is applied first.
def decorator1(func):
def wrapper():
print("Decorator 1")
return func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
return func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
Output:
Decorator 1
Decorator 2
Hello!
Here:
decorator2is applied first tosay_hello, and thendecorator1is applied to the result of that.
Use Cases for Decorators:
- Logging: Log function calls for debugging or monitoring.
- Authorization/Authentication: Check if the user is authorized to access a resource.
- Caching: Cache the results of expensive function calls to improve performance.
- Timing: Measure the time it takes for a function to execute.
- Validation: Validate the input arguments of a function.
Summary:
- A decorator is a function that modifies the behavior of another function without changing its code.
- Decorators are applied using the
@decorator_namesyntax. - They are useful for adding reusable functionality like logging, timing, validation, etc.
- Decorators can take arguments, and they can be chained to apply multiple modifications.
- Python has built-in decorators like
@staticmethod,@classmethod, and@property.
Decorators are a powerful feature in Python that allow for clean, modular, and reusable code enhancements.
Question: What are *args and **kwargs in Python?
Answer:
In Python, *args and **kwargs are used to pass a variable number of arguments to a function. They allow you to create functions that can accept an arbitrary number of positional or keyword arguments. They are very useful for writing flexible and reusable functions.
1. *args (Positional Arguments)
*args allows a function to accept any number of positional arguments (arguments that are passed without specifying the parameter names). The args variable is treated as a tuple inside the function, and you can loop through it or access individual elements like any other tuple.
- Syntax:
def function_name(*args):
Function body
- **Example**:
```python
def add(*args):
total = 0
for num in args:
total += num
return total
print(add(1, 2, 3)) # Output: 6
print(add(10, 20)) # Output: 30
In this example:
-
The
*argsin theadd()function allows it to accept any number of arguments. These arguments are accessible inside the function as a tuple, and you can iterate through them or perform operations on them. -
Key Note: The
*argsname is not fixed. You can use any name (like*values), but it is conventional to use*argsfor readability and consistency.
2. **kwargs (Keyword Arguments)
**kwargs allows a function to accept any number of keyword arguments (arguments passed in the form key=value). These arguments are passed as a dictionary inside the function, where the keys are the argument names, and the values are the corresponding argument values.
- Syntax:
def function_name(**kwargs):
Function body
- **Example**:
```python
def greet(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
greet(name="Alice", age=25)
## Output:
## name: Alice
## age: 25
In this example:
-
The
**kwargsin thegreet()function allows it to accept any number of keyword arguments. These are passed to the function as a dictionary, and you can iterate through them or access the values by key. -
Key Note: The
**kwargsname is also not fixed, but**kwargsis the standard convention.
3. Combining *args and **kwargs
You can use both *args and **kwargs in the same function to accept both positional and keyword arguments. However, the *args parameter must appear before the **kwargs parameter in the function definition.
- Syntax:
def function_name(arg1, arg2, *args, kwarg1=None, kwarg2=None, **kwargs):
Function body
- **Example**:
```python
def describe_person(name, age, *args, **kwargs):
print(f"Name: {name}")
print(f"Age: {age}")
if args:
print("Other Info:", args)
if kwargs:
print("Additional Info:", kwargs)
describe_person("Alice", 30, "Engineer", "New York", city="London", country="UK")
Output:
Name: Alice
Age: 30
Other Info: ('Engineer', 'New York')
Additional Info: {'city': 'London', 'country': 'UK'}
In this example:
*argsaccepts positional arguments that are passed as a tuple ("Engineer","New York").**kwargsaccepts keyword arguments that are passed as a dictionary ({"city": "London", "country": "UK"}).
4. Order of Parameters in Function Definition
When defining a function, the order of parameters should follow this pattern:
- Regular positional parameters
*args(optional)- Default keyword parameters (optional)
**kwargs(optional)
Here is an example that illustrates this order:
def example_function(a, b, *args, c=10, d=20, **kwargs):
print(f"a: {a}, b: {b}, args: {args}, c: {c}, d: {d}, kwargs: {kwargs}")
example_function(1, 2, 3, 4, 5, c=30, e="hello", f="world")
Output:
a: 1, b: 2, args: (3, 4, 5), c: 30, d: 20, kwargs: {'e': 'hello', 'f': 'world'}
5. Practical Use Cases of *args and **kwargs
-
*args: Useful when you want to allow a function to accept a variable number of arguments, for example when summing values or concatenating strings.Example:
def concatenate_strings(*args): return " ".join(args) print(concatenate_strings("Hello", "world!")) # Output: "Hello world!" -
**kwargs: Useful for situations where you want to pass multiple optional parameters as key-value pairs, such as when configuring a function or passing extra data to an API.Example:
def build_url(base_url, **params): url = base_url + "?" for key, value in params.items(): url += f"{key}={value}&" return url.rstrip("&") print(build_url("https://example.com", page=1, size=10)) # Output: "https://example.com?page=1&size=10"
Summary of Key Differences:
| Feature | *args | **kwargs |
|---|---|---|
| Type of Arguments | Accepts a variable number of positional arguments. | Accepts a variable number of keyword arguments (key-value pairs). |
| Usage | Used when the number of arguments is unknown and passed as a tuple. | Used when the function needs to accept named arguments dynamically. |
| Syntax | *args (tuple inside the function). | **kwargs (dictionary inside the function). |
Conclusion:
*argsand**kwargsprovide flexibility in Python function definitions, allowing you to pass an arbitrary number of arguments to a function.*argsis used for positional arguments, and**kwargsis used for keyword arguments.- These features are commonly used in functions that accept varying numbers of arguments or when extending functions, such as in decorators or in functions that need to accept extra configuration parameters.
Read More
If you can’t get enough from this article, Aihirely has plenty more related information, such as Python interview questions, Python interview experiences, and details about various Python job positions. Click here to check it out.
