Basics
What is Python? and explain its features?
How is Python interpreted?
Source Code -> Intermediate Language -> Native (or machine) Language
-> Execution
What are Python wheels?
- Python wheels are a package format for Python libraries and applications
- Designed as a successor to the older Egg format, wheels aim to improve the ease and speed of distributing Python software.
- They are a built-package format, which means they are distributions that have already been packaged in a way that is ready for installation.
- This can significantly speed up the installation process because it avoids the need for compiling and building processes which are often necessary with source distributions.
How do you create a Python package?
-
Structure the package (assuming name to be
my_pkg).
- my_pkg: root directory of the package.
- my_pkg/my_pkg: holds the actual package modules.
- my_pkg/my_pkg/__init__.py: This file makes Python treat the directories as containing packages. It can be empty or can contain package initialization code.
- my_pkg/my_pkg/<module_name>.py: Python module with actual code.
- my_pkg/tests: holds tests for the package.
- my_pkg/setup.py: specify metadata about the package and the files that should be included.
- my_pkg/README.md: Markdown file to describes the package.
-
Register the Package (optional)
- Create accounts for Test PyPI and PyPI.
- Build package: python setup.py sdist bdist_wheel. creates a source distribution and a wheel of your package in a dist/ directory.
- Upload package using Twine: pip install twine; twine upload dist/*
-
Install the Package
- Once uploaded to Twine, it can be installed using pip install my_pkg
Explain the use of the pass statement in Python.
- pass is a null operation, meaning it does nothing.
- It is used when a statement is required syntactically but you do not want any command or code to execute.
- It is often used as a placeholder in empty classes or functions.
""" Placeholder for future code """
def my_function():
pass
""" Implementing Abstract Methods """
class MyAbstractClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
""" Handling Exceptions """
try:
risky_call()
except ValueError:
pass
""" Loops or Conditional Blocks """
if condition_met:
pass
Data Structures
Explain list, dictionary and tuple?
-
List
-
Dictionary
- stores key-value pair. Stored as {key1: value1, key2: value2}
- Members can be accessed using key.
- All the keys have to be unique
-
Tuple
- Immutable list of Python objects. Hence, used to store read-only items. (item1, item2)
Explain remove, del and pop for lists
| Property | remove | del | pop |
| Working | removes first matching value | deletes element using index | removes element using index |
| Parameter required | value | index | index |
| Returned value | None | Doesn't return anything | Deleted element |
| Example |
|
|
|
Explain append vs extend
| Property | append | extend |
| No of items added | single element | all elements of iterables |
| Position at which items are added | end of the list | end of the list |
| Types of items to be added | any data structures | only elements of iterables |
Explain deep copy vs shallow copy
- Shallow Copy: Copies the reference pointers just like copying pointers.
- Deep Copy: Creates a new duplicate object and recursively adds the copies of nested objects present in the original elements.
import copy
original = [1, [2, 3], 4]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
original[1][0] = "changed"
print(shallow) # [1, ['changed', 3], 4]
print(deep) # [1, [2, 3], 4]
Explain Python's type coercion rules.
- Python's type coercion refers to the implicit conversion of values from one data type to another in an expression involving multiple types.
""" Numeric Type Coercion """
result = 3 + 2.5 # 3 is coerced to 3.0, result is 5.5 (float)
result = 2 + 3j + 5 # 5 is coerced to 5 + 0j, result is 7 + 3j (complex)
result = True + 2 # True is coerced to 1, result is 3 (integer)
""" strings cannot be implicitly converted or coerced into integers or floats during concatenation or any other operations involving strings and these types """
text = "Year: " + 2021 # Raises TypeError
text = "Year: " + str(2021) # Correct: "Year: 2021"
""" Operations involving lists and other data types do not typically result in implicit type coercion """
my_list = [1, 2, 3]
result = my_list + 2 # Raises TypeError
""" Coercion in Conditional Statements """
if (check or 0): # Numeric zero values (0, 0.0, 0j), empty sequences ([], (), "", {}), and None are considered False.
if (check and 10) # Non-zero numbers, non-empty sequences, and almost everything else are treated as True.
What is Python's None type, and how is it used?
- None is a special constant representing the absence of a value or a null value.
- It is an object of its own datatype, the NoneType.
- None is often used to signify 'empty' or 'no value here' and is commonly returned by functions that do not explicitly return a value.
- None is a singleton in Python, which means there is only one instance of None
- To check if a variable is None, you should always use is or is not, which checks for object identity.
""" Default Function Arguments """
def my_func(arg=None):
if arg is None:
arg = []
""" Optional Arguments and Keyword Arguments """
def connect(host, port=None):
if port is None:
port = 12345 # Use default port if none specified
connect("example.com")
connect("example.com", 8080)
""" Return Value for Functions that Explicitly Do Not Return Anything """
def no_return():
print("This function does not return anything.")
result = no_return()
print(result) # None
""" Variable Initialization """
result = None
if some_condition():
result = compute_result()
if result is None:
print("No result!")
else:
print("Result computed!")
""" Checking for None """
if x is None:
print("x is None")
if x is not None:
print("x has a value")
Execution
Explain Python's execution model
-
Source Code
- Everything starts with the Python source code (.py file extension).
-
Parsing
- Interpreter parses the source code into a data structure known as an Abstract Syntax Tree (AST)
- The AST represents the syntactic structure of the code in a tree-like format, where each node represents a construct occurring in the source code.
- This step checks the syntax of your code; if there's a syntax error, Python will raise an exception at this stage and halt further execution.
-
Compilation
- For the next step, the AST is compiled into bytecode.
- Bytecode is a low-level set of instructions that is executed by the Python virtual machine (PVM).
-
Python Virtual Machine (PVM)
- The PVM is the runtime engine of Python; it's an interpreter which executes the bytecode.
- The PVM handles all the dynamic aspects of Python such as loops, function calls, memory management, and exception handling.
-
Bytecode Optimization
- Python includes a peephole optimizer in its compilation process, which performs limited optimization steps on the bytecode.
- These optimizations may include eliminating unnecessary instructions, constant folding, and more
- These optimizations are relatively minor compared to those performed by compilers for statically-typed languages.
How is memory managed in Python?
- Involves a private heap containing all Python objects and Data Structures.
- Interpreter takes care of this heap, and programmer has no access to it - Allocation of heap space for Python objects is taken care of by Python memory manager.
- Built-in garbage collector recycles all unused memory.
What is the garbage collection mechanism in Python?
- It is a cyclic garbage collector that tracks objects and their references to determine which objects are no longer needed.
- When an object is no longer referenced, the garbage collector reclaims the memory used by that object.
Function
What are decorators in Python?
- Decorators are functions that modify the behavior of another function.
- They are typically used to extend the functionality of the decorated function without permanently modifying it.
def decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@decorator
def say_hello():
print("Hello!")
say_hello()
What are lambdas in Python?
What are list comprehensions and dict comprehensions and a generator?
Generator vs Iterator?
What is a Closure in Python?
A Closure is a function object that remembers values in enclosing scopes even after the outer function has finished executing.
def outer(x):
def inner(y):
return x + y
return inner
add_five = outer(5)
print(add_five(3)) # Output: 8
Abstract Method vs Virtual Method in Python?
Abstract Method
- An abstract method is a method that is declared in a class, but it does not have an implementation in that class.
- The class containing an abstract method is typically known as an abstract class, and it cannot be instantiated on its own.
- Abstract methods must be implemented by any subclass that derives from the abstract class.
- Must be declared in an abstract class using the abc module and must be implemented by any subclass.
Virtual Method
- A virtual method is a method that has an implementation in a base class but can be overridden in a derived class.
- Python handles method overriding differently and does not have the "virtual" keyword.
- In Python, all methods in classes are effectively virtual, which means any method can be overridden in a subclass.
How are arguments passed by value or by reference in Python?
Neither. Python uses a mechanism known as "pass-by-object-reference"
or "pass-by-assignment". When you pass arguments to a function in
Python, you are actually passing object references, not the actual
object itself nor an explicit pointer to it. However, these
references are passed by value. This means the function receives a
copy of the reference, pointing to the same object in memory as the
original one.
def modify_num(num):
num = num + 10
return num
def modify_lst(lst):
lst.append(4)
return lst
x = 5
print(modify(x)) # 15
print(x) # 5, unchanged because x is an immutable integer
x = [1, 2, 3]
print(modify(x)) # [1, 2, 3, 4]
print(x) # [1, 2, 3, 4], x is changed because it's mutable
Key Points
- No New Objects Are Created on Passing: When you pass an argument, Python does not create a new object; it only passes the reference. Whether the function can modify the original object depends on whether the object is mutable or immutable.
- Reassignment Does Not Affect Original: Reassigning a parameter to a new value within a function creates a new local variable. It does not affect the original variable outside the function.
File handling
What are file processing modes supported by Python?
There are 4 file processing modes supported by Python
- read-only (r)
- write-only (w)
- read-write (rw)
- append (a)
How to delete a file in Python?
Files can be deleted in Python using one of the following commands
- os.remove(filename)
- os.unlink(filename)
OOP
How to create an empty class in Python?
Use pass after the definition of the class
object
What is early binding?
Multi Threading
How does the Global Interpreter Lock (GIL) affect Python concurrency?
- GIL stands for Global Interpreter Lock.
- It is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once.
- It is necessary because CPython memory management is not thread-safe. GIL is a performance bottleneck in multi-threaded programs.
- The GIL allows only one thread to execute Python bytecode at a time, which can be a bottleneck in CPU-bound and multi-threaded programs. However, it does not affect performance in I/O-bound or multi-process applications.