Top Python 3.x Interview Questions You Must Know
Question: What is the difference between Python 2.x and Python 3.x?
Answer:
Python 2.x and Python 3.x are two major versions of the Python programming language. Python 3.x was released to fix various design flaws and provide better support for modern programming practices, but this led to backward incompatibility with Python 2.x. Below are the key differences between Python 2.x and Python 3.x:
1. Print Statement vs. Print Function
-
Python 2.x:
print
is treated as a statement.print "Hello, World!"
-
Python 3.x:
print
is treated as a function and requires parentheses.print("Hello, World!")
2. Integer Division
-
Python 2.x: Division of integers results in an integer (floor division) when both operands are integers.
3 / 2 # Output: 1
To get a float result, you would need to explicitly convert one of the operands to a float.
3 / float(2) # Output: 1.5
-
Python 3.x: Division of integers results in a float by default.
3 / 2 # Output: 1.5
If you want integer division (floor division), use the
//
operator.3 // 2 # Output: 1
3. Unicode Handling
-
Python 2.x: Strings are treated as ASCII by default unless specified as Unicode with a
u
prefix.s = "hello" # ASCII string u = u"hello" # Unicode string
-
Python 3.x: Strings are treated as Unicode by default, and byte data is handled separately with a
b
prefix for byte strings.s = "hello" # Unicode string b = b"hello" # Byte string
4. Iterators and Generators
-
Python 2.x: Functions like
range()
,map()
, andfilter()
return lists.range(5) # Output: [0, 1, 2, 3, 4]
-
Python 3.x: These functions return iterators, which are more memory efficient, especially for large datasets.
range(5) # Output: range(0, 5) (an iterator object)
To get a list in Python 3.x, you can explicitly convert the iterator into a list:
list(range(5)) # Output: [0, 1, 2, 3, 4]
5. Exception Handling Syntax
-
Python 2.x: The
except
clause usesas
or a comma.try: # some code except SomeError, e: # handle exception
-
Python 3.x: The
except
clause always usesas
to bind the exception to a variable.try: # some code except SomeError as e: # handle exception
6. Input Handling
-
Python 2.x: The
input()
function evaluates the user input as Python code, andraw_input()
is used to get a string input.x = raw_input("Enter something: ") # String input x = input("Enter a number: ") # Evaluates as Python code
-
Python 3.x: The
input()
function returns string input, andraw_input()
has been removed.x = input("Enter something: ") # Always returns a string
7. Standard Library Changes
-
Python 2.x: Many modules and functions in the standard library have different names and may not follow modern practices. For example,
urllib
andurllib2
were separate in Python 2.x. -
Python 3.x: The standard library was reorganized and many modules were renamed or consolidated. For example,
urllib
in Python 3.x is more consistent, andurllib2
was merged intourllib
.
8. Function Annotations
-
Python 2.x: Function annotations are not supported (except for some third-party libraries).
-
Python 3.x: Function annotations are supported and can be used to specify the type of parameters and return types.
def greet(name: str) -> str: return "Hello " + name
9. Libraries and Third-Party Support
-
Python 2.x: As Python 2.x reached its end of life (EOL) on January 1, 2020, many third-party libraries have stopped supporting it, and new libraries are often built exclusively for Python 3.
-
Python 3.x: Python 3 is actively maintained and supported by the community. It is the recommended version for all new projects and for those migrating from Python 2.x.
10. Performance Improvements
- Python 3.x has made numerous performance improvements over Python 2.x, including better memory handling, optimization of various functions, and enhancements to the garbage collection process. For many use cases, Python 3.x is faster than Python 2.x.
11. End of Life (EOL)
- Python 2.x: Python 2 reached its official end of life (EOL) on January 1, 2020. This means it no longer receives updates, including security fixes, from the Python Software Foundation.
- Python 3.x: Python 3.x is the future of the language and continues to receive new features, performance improvements, and security updates.
Summary of Key Differences:
Feature | Python 2.x | Python 3.x |
---|---|---|
print statement | print() function | |
Integer Division | Integer division results in an integer | Integer division results in a float |
Unicode Strings | ASCII by default, use u for Unicode | Unicode by default |
String Handling | input() evaluates input | input() returns string |
Iterators | Returns list | Returns iterator |
Exception Syntax | except Exception, e: | except Exception as e: |
Standard Library | Different module names | Reorganized and renamed modules |
Function Annotations | Not supported | Supported |
End of Life | January 1, 2020 (no more support) | Actively supported |
Conclusion:
Python 3.x introduced many improvements and fixes over Python 2.x, making it the preferred choice for new projects. However, due to the backward incompatibility, migrating from Python 2.x to Python 3.x can require significant changes in code. Python 2.x reached its end of life in 2020, so it is advisable to switch to Python 3 for all new development and maintenance of existing projects.
Question: What are Python decorators?
Answer:
In Python, decorators are a way to modify or extend the behavior of a function or method without changing its source code. A decorator is essentially a function that wraps another function (or method), and allows additional functionality to be added to it before or after its execution.
Decorators are widely used in Python for various purposes, such as logging, authentication, authorization, caching, etc.
How Do Python Decorators Work?
A decorator is a higher-order function that takes a function as input and returns a new function that adds some kind of functionality to the original function.
Basic Syntax of a Decorator:
def decorator_function(original_function):
def wrapper_function():
# Code to execute before calling the original function
print("Wrapper executed this before {}".format(original_function.__name__))
# Call the original function
return original_function()
return wrapper_function
To apply this decorator to a function, you use the @decorator_name
syntax above the function definition.
Example of a Simple Decorator:
def decorator_function(original_function):
def wrapper_function():
print("Wrapper executed before {}".format(original_function.__name__))
return original_function()
return wrapper_function
# Using the decorator
@decorator_function
def say_hello():
print("Hello!")
say_hello()
Output:
Wrapper executed before say_hello
Hello!
In this example, @decorator_function
is applied to the say_hello
function. When say_hello()
is called, the wrapper_function
is executed first, and then it calls the original say_hello
function.
Components of a Decorator:
- Outer Function: This is the decorator function that takes a function as input.
- Inner Function: This function wraps around the original function and can modify or extend its behavior.
- Returning the Inner Function: The decorator returns the inner function, which will be used in place of the original function.
Example with Arguments:
If the decorated function has parameters, the decorator should pass those parameters to the original function as well. This can be done using *args
and **kwargs
to handle any number of positional and keyword arguments.
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print("Wrapper executed before {}".format(original_function.__name__))
return original_function(*args, **kwargs)
return wrapper_function
@decorator_function
def say_hello(name):
print("Hello, {}!".format(name))
say_hello("Alice")
Output:
Wrapper executed before say_hello
Hello, Alice!
In this example, the decorator function now accepts any number of arguments (*args
and **kwargs
) and forwards them to the say_hello
function.
Using Built-in Decorators:
Python has several built-in decorators that are commonly used, such as @staticmethod
, @classmethod
, and @property
.
-
@staticmethod
:- It is used to define a method that doesn’t depend on class instance data.
class MyClass: @staticmethod def say_hello(): print("Hello from static method!")
-
@classmethod
:- It is used to define a method that operates on the class, not the instance. It takes the class as its first argument (
cls
) instead of the instance (self
).
class MyClass: @classmethod def greet(cls): print("Hello from class method!")
- It is used to define a method that operates on the class, not the instance. It takes the class as its first argument (
-
@property
:- It allows you to define a method as an attribute, so that it can be accessed like an attribute but executed as a method.
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @property def area(self): return 3.1416 * self._radius * self._radius
Chaining Decorators:
You can apply multiple decorators to a single function by stacking them, with the bottom-most decorator applied first.
def decorator_one(func):
def wrapper():
print("Decorator One")
func()
return wrapper
def decorator_two(func):
def wrapper():
print("Decorator Two")
func()
return wrapper
@decorator_one
@decorator_two
def say_hello():
print("Hello!")
say_hello()
Output:
Decorator One
Decorator Two
Hello!
In this case, say_hello()
is first passed through decorator_two
, then the result of that is passed to decorator_one
.
Practical Use Cases for Decorators:
-
Logging:
- You can use decorators to log function calls, execution times, or results.
def log_function_call(func): def wrapper(*args, **kwargs): print(f"Calling function {func.__name__} with arguments {args}, {kwargs}") result = func(*args, **kwargs) print(f"{func.__name__} returned {result}") return result return wrapper @log_function_call def add(a, b): return a + b
-
Authorization:
- You can use decorators to check if the user has the proper permissions before running a function.
def check_permissions(func): def wrapper(*args, **kwargs): if not has_permission(): raise PermissionError("You don't have permission to perform this action") return func(*args, **kwargs) return wrapper
-
Caching:
- You can cache results of expensive function calls using decorators, which can help improve performance.
def cache_results(func): cache = {} def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper
Summary:
- Python decorators are a powerful feature that allows you to add functionality to functions or methods without modifying their actual code.
- They work by defining a function that takes another function as input and returns a new function (the wrapped function).
- Decorators are often used for logging, access control, memoization (caching), and modifying or enhancing functions in a modular way.
- Decorators are a great tool for adhering to the DRY principle (Don’t Repeat Yourself), as they allow you to apply reusable behavior to multiple functions.
Read More
If you can’t get enough from this article, Aihirely has plenty more related information, such as Python 3.x interview questions, Python 3.x interview experiences, and details about various Python 3.x job positions. [Click here](https://www.aihirely.com/tag/Python 3.x) to check it out.
Tags
- Python 3.x
- Python 2.x vs Python 3.x
- Python Decorators
- Shallow Copy
- Deep Copy
- Python Self Keyword
- Staticmethod vs Classmethod
- Python Memory Management
- Python Generators
- Python Yield
- List Comprehensions
- Python Data Types
- Python with Statement
- Python Exception Handling
- Global Interpreter Lock (GIL)
- Python Built in Functions
- Python Lambda Functions
- Python Pass Statement
- Python Modules
- Python Variable Scope
- Python Namespaces
- Python List Methods
- Python Remove vs Del
- Python Functions
- Python Advanced Concepts
- Python Key Features