Python Institute · Free Practice Questions · Last reviewed May 2026
24real exam-style questions organised by domain, each with the correct answer highlighted and a plain-English explanation of why it's right — and why the others are wrong.
12% of exam · 6 sample questions below
A Python script imports the module 'my_module'. The developer wants to ensure that when the script is run directly, it executes a specific function, but when imported as a module, that function is not executed. Which code snippet achieves this?
if __name__ == '__main__': run()
This is the canonical Python idiom for conditional execution. The interpreter assigns the special variable __name__ the value '__main__' only when the source file is run directly as the main program (e.g., `python my_module.py`). When the file is imported as a module, __name__ becomes the module's fully qualified name, so the equality check fails and run() is not invoked, allowing safe import without side effects.
if __name__ == '__main__': run()
This option is exactly equivalent to the standard guard in option A, and it is just as correct. Comparing `__name__` to the string literal '__main__' is the recognized, PEP 8-compliant way to determine whether a file has been executed directly rather than imported. The identical code appearing twice in the options does not change its validity; both instances pass because the interpreter sets __name__ consistently regardless of how many times the idiom appears.
if os.environ.get('RUN_MAIN'): run()
if sys.argv[0] == 'my_module': run()
A developer notices that a custom package 'mypackage' is not being found when importing, even though it is installed in the site-packages directory. The developer suspects a conflict with another package of the same name. Which command should the developer run to diagnose the location from which Python is importing the package?
print(mypackage)
print(__file__)
print(mypackage.__file__)
For an imported module, the `__file__` attribute stores the filesystem path of the source file from which the module was loaded. When `mypackage` is a package, its `__file__` points to the package's `__init__.py` file, which is exactly the location of the package on disk in most ordinary cases. This is the standard, programmatic way to determine where a package or module resides, making this option correct.
import os; print(os.getcwd())
Which TWO of the following are valid ways to import a module named 'math' and give it an alias 'm'?
from math import * as m
import math as m
Correct syntax: `import math as m` imports the full math module with alias m.
import math m
import math alias m
from math import sin as m
Which THREE of the following statements about Python packages and modules are true?
The sys.path list is read-only and cannot be modified at runtime.
A package must contain an __init__.py file to be importable.
A module is a single .py file containing Python definitions and statements.
This is the definition of a module.
The __all__ variable defines the public API of a module or package.
__all__ controls what is exported with 'from module import *'.
Relative imports use dots to refer to the current and parent packages.
A single dot refers to current package, two dots to parent, etc.
You are a developer at a company that builds a data processing pipeline. The pipeline consists of several Python modules organized in a package called 'pipeline'. The package structure is:
pipeline/ __init__.py load.py transform.py analyze.py
The pipeline is deployed on a server where Python 3.8 is installed. The server also has a globally installed package called 'pipeline' (from a different project) in the site-packages directory. When you run your scripts that import 'pipeline', you get unexpected behavior because Python is importing the wrong package. You need to ensure that your local 'pipeline' package is used instead of the global one. You cannot uninstall the global package because it is used by another application. You have the following options:
A) Modify the PYTHONPATH environment variable to include the directory containing your 'pipeline' package before the site-packages directory. B) Rename your local 'pipeline' package to something else and update all imports. C) Use a virtual environment specific to your project and install your package there. D) Add an __init__.py file with a special import hook to override the global package.
Which course of action is the most appropriate and reliable?
Modify the PYTHONPATH environment variable to include the directory containing your 'pipeline' package before the site-packages directory.
Rename your local 'pipeline' package to something else and update all imports.
Use a virtual environment specific to your project and install your package there.
Using a virtual environment creates an isolated environment, ensuring your local package is used without affecting or being affected by the global package. This is the standard and most reliable approach.
Add an __init__.py file with a special import hook to override the global package.
A Python package 'mypackage' contains the following hierarchy:
mypackage/ __init__.py subpackage1/ __init__.py module_a.py subpackage2/ __init__.py module_b.py
From a script outside the package, a programmer writes:
import mypackage.subpackage1.module_a
Which statement is true about the import?
Only mypackage/__init__.py is executed.
No __init__.py files are executed because the import uses a dotted path.
After the import, 'mypackage' is not available as a name in the namespace.
Both mypackage/__init__.py and mypackage/subpackage1/__init__.py are executed.
When importing `mypackage.subpackage1`, Python executes the `__init__.py` of each package along the dotted path to initialize them as proper packages. This happens because the import system processes each component sequentially: first `mypackage` is imported, which runs its `__init__.py`, then its submodule `subpackage1` is imported, which runs its own `__init__.py`. This two-step initialization is fundamental to Python's package system, ensuring parent packages are fully loaded before their subpackages.
Want more Modules and Packages practice?
Practice this domainA developer needs to count the number of occurrences of the substring 'is' in the string 'This is a test. Is this a test?'. Which code correctly performs the count?
'This is a test. Is this a test?'.split().count('is')
'This is a test. Is this a test?'.count('is')
Correctly counts overlapping? No, count does not count overlapping, but 'is' appears at positions 5 and 17, not overlapping, so returns 2.
'This is a test. Is this a test?'.index('is')
'This is a test. Is this a test?'.find('is')
Which THREE are valid ways to create a multiline string in Python?
s = ('Line1\n' 'Line2')
This is correct because Python implicitly concatenates adjacent string literals at compile time. The expression ('Line1\n' 'Line2') produces the single string 'Line1\nLine2', where \n is a single escape character representing a line break. When printed, the result appears on two lines, so it is a valid multiline string. The parentheses are not required but help break long lines for readability.
s = """Line1 Line2"""
This is correct because triple double quotes, like triple single quotes, let a string span multiple physical source lines while preserving the embedded line breaks exactly as typed. The resulting string contains a newline character at the end of the first line, so it behaves as a genuine multiline string. This syntax is the standard choice for docstrings and is also convenient for writing SQL or long formatted text blocks in code.
s = '''Line1 Line2'''
This is correct because triple single quotes allow a string literal to contain literal newline characters, meaning the physical line break between Line1 and Line2 is preserved as part of the string value. It is equivalent to writing 'Line1\nLine2' but is more readable when the string is long. Additionally, triple single quotes permit embedded single quotes without escaping, giving it flexibility for multiline text.
s = "Line1\ Line2"
s = 'Line1 Line2'
Which THREE methods return a boolean value?
str.upper()
str.startswith()
Returns True or False.
str.islower()
Returns True or False.
str.isalpha()
Returns True or False.
str.find()
You are a data analyst working with a dataset of customer reviews. Each review is stored as a string in a list. You need to count how many reviews contain the word 'excellent' (case-insensitive). However, the word might appear as 'Excellent', 'EXCELLENT', or even with punctuation like 'excellent!'. The current code uses 'excellent' in review.lower(), but this fails if 'excellent' is part of another word like 'unexcellent'. You need to ensure that only the whole word 'excellent' is counted. Which code modification will correctly count whole word occurrences?
Use re.search(r'\bexcellent\b', review, re.IGNORECASE)
The \b word boundary anchors ensure that 'excellent' is matched only when it stands as its own word, not as a substring of a larger token, while the re.IGNORECASE flag makes the match case-insensitive. Because re.search scans the entire string but the boundary restricts the match position, this option correctly finds 'Excellent', 'excellent.', and 'excellent' while rejecting 'unexcellent'. This is the only approach that combines whole-word semantics with case-insensitive matching in a single call.
Use 'excellent' in review.lower().split()
Use review.lower().count('excellent') > 0
Use review.lower().find('excellent') != -1
A developer needs to parse a log file where each line contains a timestamp followed by a message. The timestamp format is 'YYYY-MM-DD HH:MM:SS'. Which string method is most appropriate to split the timestamp from the message?
str.rsplit()
str.splitlines()
str.partition()
str.split()
str.split() with no arguments splits on any run of whitespace, trimming leading and trailing spaces, and returns a list of non-empty substrings. For a log line like '2025-04-10 14:22:31 INFO message here', the timestamp (which contains no spaces) becomes the first element while the rest of the line is broken into subsequent elements, cleanly isolating the timestamp. It is the most direct method because it handles variable amounts of whitespace without requiring a separator to be specified.
A programmer writes a function that expects a string and returns it reversed. Which code snippet correctly reverses the string 'stressed' to 'desserts'?
result = s.reversed()
result = s[::-1]
Using extended slice syntax with a step of `-1` creates a reversed copy of the entire string: `s[::-1]` means start at the end, go to the beginning, and step backward by one. This is the most idiomatic and concise way to reverse a string in Python, and it is often preferred for its readability and speed. Since strings are immutable, this operation allocates a new string object containing the characters in reverse order, leaving the original string unchanged.
s.reverse()
result = ''.join(reversed(s))
This is correct: `reversed(s)` is a built-in function that returns a reverse iterator over the characters of `s`, and `''.join()` consumes that iterator, concatenating each character with an empty separator to form a new string. This approach works for any finite sequence, not just strings, and it is memory-efficient because the iterator yields characters lazily rather than constructing an intermediate list. The resulting expression is explicit and clear, making it a reliable way to reverse a string.
Want more Strings practice?
Practice this domain34% of exam · 6 sample questions below
A developer creates a Python class with a method that is intended to be overridden in subclasses. Which approach best ensures that the method is not accidentally called on the base class?
Use 'pass' as the method body
Delete the method from the base class using 'del'
Add a comment '# override in subclass' inside the method body
Raise NotImplementedError inside the method body
Raising NotImplementedError clearly signals the method must be overridden.
A Python class 'BankAccount' has a method 'withdraw(amount)' that deducts 'amount' from 'self.balance'. A developer writes a subclass 'SavingsAccount' that overrides 'withdraw' to add a penalty if balance drops below minimum. Which design pattern is being used?
Composition
Aggregation
Method overriding
The subclass provides a specific implementation of the inherited method.
Inheritance
A developer writes a class 'Logger' with a class method 'log(msg)' that writes to a file. Another class 'AppLogger' inherits from 'Logger'. The developer expects both classes to share the same file handle. However, after creating an instance of 'AppLogger', the file handle is different. What is the most likely cause?
The 'log' method is defined as a class method using @classmethod
The file handle is opened in the __init__ method of the base class
Opening the file handle inside __init__ assigns the result to an instance attribute (via self), so each time a new Logger or subclass object is created, a separate descriptor is opened and stored on that specific instance. Because the handle is not attached to the class object, no sharing occurs between instances. This directly contradicts the premise that a single logger's file handle is shared, making this the correct flaw in the developer's code.
The file handle is stored as a private attribute __file
The subclass overrides the 'log' method
A Python class 'Shape' defines an abstract method 'area'. Subclasses 'Circle' and 'Square' implement 'area'. A function 'calculate_area(shape)' expects a 'Shape' instance. Which principle ensures that the function works correctly without knowing the specific subclass?
Interface Segregation Principle
Liskov Substitution Principle
The Liskov Substitution Principle (LSP) asserts that any subclass must be able to replace its base class without altering the correctness of the program. When Shape declares an abstract area() method, it establishes a behavioral contract that every subclass must honor; if a subclass overrides area() with an incompatible return type, raises an unexpected exception, or changes invariants, it breaks substitutability. This is exactly the design concern addressed by LSP, making it the correct principle for the described scenario.
Single Responsibility Principle
Dependency Inversion Principle
Which TWO of the following are valid ways to define a class attribute that is shared by all instances?
class MyClass: attr: int = 0
class MyClass: def set_attr(self): MyClass.attr = 0
class MyClass: pass MyClass.attr = 0
Assigns a class attribute after definition.
class MyClass: attr = 0
Defines a class attribute directly.
class MyClass: def __init__(self): self.attr = 0
A Python developer is implementing a class that should behave like a sequence and support indexing. Which pair of special methods must be defined to achieve this?
__getitem__ and __contains__
__setitem__ and __delitem__
__iter__ and __next__
__getitem__ and __len__
According to the Python data model, a sequence is defined primarily by having a `__len__` method and a `__getitem__` method that accepts integer indices (and optionally slices). Together they give the object a finite length, support `obj[i]` indexing, and implicitly enable iteration via the old-style `__getitem__` fallback. On top of these two, the `collections.abc.Sequence` ABC automatically mixes in `__contains__`, `__iter__`, `__reversed__`, `index`, and `count`, showing how much behavior is derived from just these two methods.
Want more Object-Oriented Programming practice?
Practice this domain36% of exam · 6 sample questions below
A developer implements a custom exception class `DataError` that inherits from `Exception`. Which method override is essential to ensure the exception message is properly displayed when caught?
Override __init__ to accept a message and call super().__init__(message).
This ensures the message is stored and displayed.
Set the __cause__ attribute in __init__.
Override __str__ to return a formatted string.
Override __repr__ to return a detailed representation.
Which of the following statements about the `finally` block is true?
It executes only if no exception is raised.
It does not execute if a return statement is in try block.
It executes only if an exception is raised.
It always executes, regardless of exceptions.
Finally is guaranteed to run.
What is the output of the Python code after reading the config.txt file?
8080 (as string)
An exception is raised.
8080
This is correct because the code reads the configuration value and converts it to an integer using int() (or a ConfigParser getint() call). The integer 8080 is then passed to print(), which displays the number without quotes. Since the conversion succeeds, the output is exactly the integer 8080.
'8080'
Drag and drop the steps to serialize a Python object to JSON using the json module into the correct order.
Step 1: Import the json module. Step 2: Create a Python dictionary. Step 3: Open a file in write mode. Step 4: Use json.dump() to write the dictionary to the file.
This sequence is correct because the json module must be imported before any of its functions, such as json.dump(), can be referenced. The Python dictionary must then exist as the data to be serialized, followed by opening a file in write mode to provide a writable file-like object. Only after the file is successfully opened can json.dump() be called, as it writes the serialized JSON directly to that open handle. Attempting to run json.dump() without an open file would raise a TypeError or AttributeError, so the given order ensures every dependency is satisfied before the critical call.
Step 1: Create a Python dictionary. Step 2: Import the json module. Step 3: Open a file in write mode. Step 4: Use json.dump() to write the dictionary to the file.
Step 1: Import the json module. Step 2: Open a file in write mode. Step 3: Create a Python dictionary. Step 4: Use json.dump() to write the dictionary to the file.
Step 1: Import the json module. Step 2: Create a Python dictionary. Step 3: Use json.dump() to write the dictionary to the file. Step 4: Open a file in write mode.
Match each Python data structure to its mutability.
list: Mutable
A list is a mutable, heterogeneous sequence stored in a contiguous block of memory. Its mutability means you can modify it in place without creating a new object: assign to an index or slice, append, pop, insert, extend, or remove elements via methods like append() and pop(). Because the underlying memory address remains the same throughout such operations, lists are suitable as dynamic collections that need frequent structural changes.
tuple: Mutable
dict: Mutable
A dict is a mutable, unordered mapping stored as a hash table. Its mutability enables in-place alteration of key-value bindings: you can assign d[key] = value to add or replace an entry, use del d[key] or pop() to remove one, and employ methods like update() and setdefault() to modify the mapping. Because the hash table is allocated and resized dynamically, the dict object identity persists through these mutations, making it an efficient key-based associative container.
set: Immutable
string: Immutable
A string is an immutable sequence of Unicode code points. No operation can alter the characters of an existing str object; each method that appears to modify a string—such as replace(), upper(), or strip()—actually constructs and returns a new string, leaving the original intact. This immutability guarantees that strings can be safely hashed and shared across threads, and it enables internal optimizations like interning, though it also means repeated concatenation in a loop creates many intermediate objects, making ''.join() the preferred approach.
frozenset: Mutable
A developer writes a function that reads a file and processes its content. The function should handle the case where the file does not exist without catching other I/O errors. Which exception should be caught?
PermissionError
IOError
OSError
FileNotFoundError
FileNotFoundError is the specific built-in exception that Python raises when an attempt to open or access a file path cannot succeed because the path does not exist, typically with errno ENOENT. For a read operation, open(path, 'r') will immediately raise it if the file is not present before any other processing occurs. Because it is narrowly scoped, catching FileNotFoundError lets the developer provide exactly the right fallback—like creating the file or printing a meaningful message—without masking permission problems or unrelated OS failures.
Want more Exceptions and File I/O practice?
Practice this domainThe PCAP exam has 40 questions and must be completed in 65 minutes. The passing score is 700/1000.
Scenario-based questions covering exam objectives with detailed answer explanations.
The exam covers 4 domains: Modules and Packages, Strings, Object-Oriented Programming, Exceptions and File I/O. Questions are weighted by domain — higher-weight domains appear more on your actual exam.
No. These are original exam-style practice questions written against the official Python Institute PCAP exam objectives. They are not copied from the real exam. Courseiva focuses on genuine understanding, not memorisation of braindumps.
Courseiva tracks your accuracy per domain and routes you toward weak areas automatically. Free, no account required.