Wayground logo

Free Printable Worksheets

Font size

S
M
L
XL
Worksheets

PCAP Practical Study Questions

Total questions: 50

Worksheet time: 25mins

Name
Class
Date
1.

Truthiness — Which values evaluate to False in Python without being explicitly False?

a)

Empty sequences/collections (e.g., '', [], {}, set())

b)

Zero of numeric types (e.g., 0, 0.0, 0j)

c)

None

d)

NaN from the math or decimal modules

e)

Custom objects whose __bool__ returns False

2.

is vs == — When should you use is vs == in comparisons (for example, with None, small ints, or strings)?

a)

Use is for identity checks (e.g., None), and == for value equality; do not rely on interning of small ints/strings.

b)

Use is for strings and numbers, and == for objects like None.

c)

Use == for identity checks and is for value equality.

d)

Use is and == interchangeably because CPython interns all small values.

3.

None checks — What is the Pythonic way to test for None and why?

a)

Compare with == None because it is readable and reliable.

b)

Use is None / is not None because None is a singleton and identity is intended.

c)

Call bool(None) to check falsiness because None is falsey.

d)

Use type(x) is NoneType to avoid truthiness pitfalls.

4.

Short-circuit — Given a() and b(), when will b() not execute, and what about a() or b()?

a)

In a() and b(), b() is skipped if a() is truthy; in a() or b(), b() is skipped if a() is falsy.

b)

In a() and b(), b() always executes; in a() or b(), b() never executes.

c)

In a() and b(), b() is skipped if a() is falsy; in a() or b(), b() is skipped if a() is truthy.

d)

In both cases b() always executes.

5.

Slicing basics — What does s[::-1] do, what does s[:], and why does s[:3] not raise if s is shorter?

a)

s[::-1] reverses a sequence; s[:] makes a shallow copy; slicing is clamped to bounds so s[:3] returns the available elements.

b)

s[::-1] rotates by one; s[:] returns the same reference; s[:3] raises IndexError if out of range.

c)

s[::-1] sorts descending; s[:] deep-copies; s[:3] pads with None.

d)

s[::-1] drops last element; s[:] copies metadata only; s[:3] repeats elements to fill length.

6.

Slice assignment — How can slice assignment modify multiple elements at once in a list?

a)

By assigning an iterable to a slice range, replacing that section with elements of the iterable.

b)

By calling list.replace on the slice.

c)

By using a for loop with enumerate only.

d)

By using list.extend on a single index.

7.

Copy pitfalls — Why does list2 = list1 not copy, and how do you properly copy shallow vs deep?

a)

list2 = list1 aliases the same object; use list1[:] or list(list1) for shallow copy, and copy.deepcopy for deep copy.

b)

list2 = list1 creates a new list; use list.copy for deep copy only.

c)

list2 = list1 copies values but not length; use tuple(list1) for deep copy.

d)

list2 = list1 fails due to scope; use eval to copy dynamically.

8.

Mutable defaults — Why are mutable default arguments dangerous and how do you avoid the trap?

a)

They are re-evaluated each call causing extra allocations; avoid by using global variables.

b)

They persist across calls and accumulate mutations; avoid by using None as default and create a new object inside.

c)

They cause syntax errors at runtime; avoid by using try/except.

d)

They prevent recursion; avoid by passing *args.

9.

Argument passing — How does Python pass arguments (objects by assignment/aliasing)?

a)

Pass-by-value for immutable and pass-by-reference for mutable.

b)

Pass-by-assignment (object references are passed; names are bound to the same object).

c)

Call-by-name semantics like some functional languages.

d)

Copy-on-write for all arguments.

10.

*args/**kwargs — What do *args and **kwargs capture and what is the typical ordering in a signature?

a)

*args captures keyword arguments and **kwargs captures positional ones; order is **kwargs before *args.

b)

*args captures extra positional arguments and **kwargs captures extra keyword arguments; order is positional, *args, keyword-only, **kwargs.

c)

*args captures defaults while **kwargs captures requireds; order is **kwargs, *args.

d)

Both capture only required parameters; order is arbitrary.

11.

Parameter kinds — What are positional-only, positional-or-keyword, keyword-only, and var-pos/var-key parameters?

a)

Four kinds: positional-only (before /), positional-or-keyword, keyword-only (after *), and var-pos (*args)/var-key (**kwargs).

b)

Only two kinds: positional and keyword; var-pos/var-key are not parameters.

c)

Three kinds: positional-only, keyword-only, and optional-only; var-pos/var-key are decorators.

d)

Five kinds including type-only and default-only parameters.

12.

Unpacking — How do starred expressions work for unpacking (for example, a, *mid, b = seq)?

a)

The star target collects any number of middle items into a list; ends bind to first and last items.

b)

Starred unpacking assigns each element to separate names only if lengths match exactly.

c)

Starred unpacking creates tuples automatically for all names.

d)

Starred unpacking is allowed only on the right side of assignment.

13.

for-else — When does the else clause on a loop execute, and what is a common use case?

a)

It runs whenever the loop body runs at least once; use it to print after iteration.

b)

It runs only if the loop terminates normally without a break; common for search-not-found logic.

c)

It runs only when an exception occurs; used for error handling.

d)

It runs on every iteration; used for logging.

14.

while-else — How does while ... else differ from for ... else in practice?

a)

They are identical; else runs only if no break occurs in either form.

b)

while ... else runs regardless of break, while for ... else depends on exceptions.

c)

for ... else runs only with continue statements; while ... else runs only without continue.

d)

while ... else runs only when the condition is initially False; for ... else runs only when the iterable is empty.

15.

Exception order — Why should except Exception come last, and how can you catch multiple exceptions?

a)

Specific handlers must follow general ones; catch multiples using a tuple in a single except.

b)

General handlers must come first to short-circuit; catch multiples using a list.

c)

Place except Exception first to ensure cleanup; catch multiples with bitwise OR.

d)

Order does not matter; catch multiples with multiple except blocks separated by commas.

16.

try/except/else/finally — What is the exact execution order and when does else run?

a)

try runs; if no exception, else runs; finally runs always; except runs if an exception occurs.

b)

finally runs first; else runs on exceptions; try runs last.

c)

except runs before try; else runs when an exception occurs; finally runs only if no exception.

d)

try is skipped; else runs on syntax errors; finally runs never.

17.

Raising exceptions — How do you re-raise the current exception, and when should you use raise from?

a)

Use raise with no arguments to re-raise; use raise from to chain exceptions with an explicit cause.

b)

Use return to re-raise; use raise from only in logging.

c)

Use raise(e) always; never use raise from.

d)

Use throw(e) to re-raise; use raise from to suppress context.

18.

Context managers — What protocol enables with statements and how do you write one manually?

a)

The iterator protocol; implement __iter__ and __next__.

b)

The descriptor protocol; implement __get__ and __set__.

c)

The context manager protocol; define __enter__ and __exit__ or use contextlib.contextmanager.

d)

The buffer protocol; implement __buffer__ methods.

19.

File I/O safety — What guarantees does with open(...) as f provide and why is it preferred?

a)

It ensures the file is closed even if exceptions occur; preferred for deterministic resource cleanup.

b)

It speeds up reads by caching; preferred for performance.

c)

It prevents file corruption by locking; preferred for concurrency.

d)

It converts text to UTF-8 automatically; preferred for portability.

20.

Enumerate — How do you iterate with indexes safely using enumerate, and how can you change the starting index?

a)

Use enumerate(iterable, start=0) by default; set start to a custom integer like 1 to begin at a different index.

b)

Use range(len(iterable)) always; cannot change starting index.

c)

Use enumerate only on dicts; start is a boolean.

d)

Use enumerate(iterable) but indexes are 1-based and fixed.

21.

Zip — What length does zip produce for unequal inputs, and how do you zip longest?

a)

zip stops at the shortest input length; use itertools.zip_longest to zip to the longest.

b)

zip pads missing items with None; use zip_longest to stop at shortest.

c)

zip always produces the longest length by repeating last items; use chain to stop early.

d)

zip truncates to median length; use map to extend.

22.

Comprehension scope — Why do list comprehension variables not leak into the outer scope in Python 3?

a)

They execute in their own implicit function scope.

b)

They execute in a separate local scope for the comprehension expression, not the surrounding scope.

c)

They are compiled as globals and then deleted.

d)

They are replaced by temporary names in the surrounding scope.

23.

Dict views — How are dict.keys(), dict.values(), and dict.items() dynamic during mutation?

a)

They snapshot at creation and never change.

b)

They are dynamic view objects that reflect subsequent mutations of the dict.

c)

They convert to lists automatically when mutated.

d)

They raise exceptions if the dict changes.

24.

Dict ordering — From which Python version are dicts insertion-ordered, and why does it matter?

a)

Since Python 2.7; matters for hashing performance.

b)

Since Python 3.7 (as a language guarantee after 3.6 behavior); matters for predictable iteration order and APIs.

c)

Since Python 3.3; matters only for memory usage.

d)

Since Python 3.11; matters for typing annotations.

25.

Set behavior — Why are sets unordered and how do they handle duplicates and unhashables?

a)

Sets are hash-based with no defined order; they automatically remove duplicates and cannot contain unhashable items.

b)

Sets preserve insertion order; duplicates are allowed and removed only on request.

c)

Sets are tree-based with sorted order; they accept any objects.

d)

Sets are list-like; duplicates are kept and order matters.

26.

Hashability — Why can tuples be set/dict keys but lists cannot, and when is a tuple unhashable?

a)

Tuples are immutable and hashable; lists are mutable; a tuple is unhashable if it contains any unhashable (e.g., a list) inside.

b)

Tuples are smaller; lists are larger; tuples are unhashable when empty.

c)

Tuples are always hashable regardless of contents; lists are hashable only when frozen.

d)

Lists are immutable; tuples are mutable; tuples become unhashable when nested.

27.

Sorting basics — What is the difference between sorted() and list.sort(), and what about stability and the key parameter?

a)

sorted returns a new list; list.sort sorts in place; both are stable; key functions customize sort order.

b)

sorted sorts in place; list.sort returns a new list; both are unstable.

c)

sorted returns a generator; list.sort returns None; key is unsupported.

d)

Both return new lists; stability depends on random seed.

28.

Sorting tuples — How does sorted(list_of_tuples) order by default, and how can you change the key?

a)

By lexicographic order of tuple elements; change using key=lambda t: t[i] or another function.

b)

By tuple length only; change using reverse=True exclusively.

c)

By hash value; change using cmp parameter.

d)

By random order; change using shuffle.

29.

Min/max with key: How do you find the minimum item by a computed metric using key?

a)

Use min(items, key=metric) so the item with the smallest metric is returned

b)

Map metric over items and call min() on the mapped values, then look up the original item

c)

Sort items by metric and take the first element only

d)

Use functools.reduce to track the smallest metric manually

30.

Ranges: What does range(a, b, s) include or exclude, and how is membership tested efficiently?

a)

Includes a and excludes b; x in range(a, b, s) uses arithmetic checks in O(1) without materializing

b)

Includes both a and b; x in range(a, b, s) builds a list to check containment

c)

Excludes both a and b; x in range(a, b, s) expands the sequence fully

d)

Includes a and includes b if (b − a) is divisible by s; membership is O(n)

31.

Iterators vs iterables: What distinguishes an iterator from an iterable and which methods are required?

a)

An iterator implements __iter__ returning self and __next__; an iterable implements __iter__ returning a fresh iterator

b)

An iterator only implements __next__; an iterable only implements __len__

c)

An iterator implements __iter__ returning a new iterator; an iterable implements __next__

d)

An iterator and iterable are identical; both require only __getitem__

32.

Generator basics: How do generators save memory and how can values or errors be sent into them?

a)

Generators yield lazily so they don’t store all results; use .send() to pass in values and .throw() to inject exceptions

b)

Generators precompute all outputs; use .put() to pass values and .raise() for errors

c)

Generators save memory by caching results; use .append() and .error()

d)

Generators stream from files only; values and errors cannot be injected

33.

Yield from: What does yield from do for generator composition?

a)

Delegates to a subgenerator, forwarding values, exceptions, and the subgenerator’s return value

b)

Concatenates two generators into one list

c)

Buffers yielded values to improve performance only

d)

Forces eager evaluation of a nested generator

34.

Deep vs shallow: How does copy.deepcopy handle nested objects differently from copy.copy?

a)

deepcopy recursively copies the entire structure; copy performs a shallow copy that reuses nested references

b)

deepcopy and copy are identical for immutable types only

c)

deepcopy copies references while copy copies values

d)

deepcopy is faster but less accurate than copy for nested containers

35.

String immutability: What are the consequences of strings being immutable for operations like concatenation?

a)

Every change creates a new string; repeated concatenation in loops can be costly due to repeated allocations

b)

Immutability allows in‑place edits that are O(1)

c)

Concatenation mutates the original string without allocating

d)

Immutability prevents slicing or joining altogether

36.

Joining/splitting: When should "sep".join(seq) be preferred over concatenation in loops?

a)

Prefer join for assembling many substrings because it is linear time and avoids repeated reallocations

b)

Prefer join only when the separator is an empty string

c)

Concatenation in loops is faster than join for long lists

d)

Join is only for splitting strings, not building them

37.

F-strings: How do you format numbers (width, precision) and debug with f'{var=}'?

a)

Use format specifiers like f"{x:8.2f}" for width and precision; f"{var=}" shows the name and value

b)

Width and precision are unsupported; f"{var=}" prints only the variable name

c)

Use f"{x:%8.2}"; f"{var=}" shows the type only

d)

Use f"{x:prec=2,width=8}"; f"{var=}" requires logging

38.

LEGB: Summarize LEGB scope resolution and a common pitfall with nonlocal and global.

a)

Names resolve in Local, Enclosing, Global, Builtins; assigning to an enclosing/global name requires nonlocal/global declarations

b)

LEGB means Local, External, General, Base; nonlocal/global are optional and inferred

c)

LEGB applies only to classes; functions ignore it

d)

Names resolve in Global first; nonlocal/global declarations are never needed

39.

Name shadowing: How can inner variables shadow outer ones and how do you avoid accidental shadowing?

a)

An inner assignment hides the outer name in its scope; avoid by using distinct names or explicit nonlocal/global

b)

Shadowing happens only across modules; avoid by reimporting

c)

Inner scopes cannot shadow outer ones in Python

d)

Avoid shadowing by turning all variables into attributes

40.

MRO basics: What is method resolution order and why does it matter in multiple inheritance?

a)

MRO is the linearized order Python uses to look up attributes; it ensures consistent multiple inheritance via C3 and is inspectable with Class.mro()

b)

MRO is a runtime type checker for methods only

c)

MRO determines only which __init__ runs and ignores other attributes

d)

MRO randomizes the lookup order to improve speed

41.

Class vs instance attrs: How do class attributes differ from instance attributes and what are common gotchas?

a)

Class attributes are shared across instances; instance attributes live per object; mutable class attributes are shared and can surprise

b)

Class attributes are copied into each instance automatically

c)

Instance attributes override class attributes only for methods

d)

Class attributes cannot be accessed from instances

42.

__repr__ vs __str__: When is each called and what is a good rule of thumb for implementing them?

a)

__repr__ is for an unambiguous developer representation and is used by the interactive prompt; __str__ is user‑friendly; implement __repr__ first and let __str__ fall back if needed

b)

__str__ is used by repr(); __repr__ prints user‑friendly output only

c)

Implement only __str__; __repr__ is deprecated

d)

Both are identical and always return the same string

43.

Equality hooks: How do you implement __eq__ and why also implement __hash__ for dict/set keys?

a)

Define __eq__ for logical equality and provide a consistent __hash__ (usually from immutable fields) so equal objects hash the same for dict/set keys

b)

Implement __eq__ only; hashing is automatic and unrelated

c)

Use __hash__ without __eq__ so equality always falls back to identity

d)

Define __eq__ to compare types only and never values

44.

Iterable protocol: Which dunder methods enable iteration and containment checks?

a)

Iteration uses __iter__ and __next__; containment prefers __contains__ and otherwise iterates via __iter__

b)

Only __len__ is required for both iteration and containment

c)

Containment uses __hash__ and __eq__ exclusively

d)

Iteration relies on __getitem__ with integer indexes only

45.

Contextlib utilities: What do contextlib.contextmanager and suppress() provide?

a)

contextmanager turns a generator into a context manager; suppress() creates a context that ignores specified exceptions

b)

contextmanager registers global handlers; suppress() logs exceptions

c)

contextmanager yields a thread; suppress() retries operations

d)

Both are deprecated in favor of withopen()

46.

Pathlib vs os: Why prefer pathlib for file paths and what are simple examples of common operations?

a)

Pathlib provides object‑oriented paths like Path('a') / 'b', Path.read_text(), Path.iterdir(); os uses string paths and separate functions

b)

Pathlib is slower and should be avoided; os always returns Path objects

c)

Pathlib only works on Windows; os works cross‑platform

d)

Pathlib cannot read files; it only builds paths

47.

Time complexity: What are the Big‑O costs for membership in list, set, dict; sorting; slicing; append/pop?

a)

Membership: list O(n), set/dict average O(1); sorting O(n log n); slicing O(k); append amortized O(1); pop from end O(1)

b)

Membership: list O(1), set/dict O(n); sorting O(n); slicing O(1); append O(n); pop O(n)

c)

Membership: all O(log n); sorting O(n log n); slicing O(n^2); append O(log n); pop O(log n)

d)

Membership: list , set/dict ; sorting ; slicing ; append ; pop

48.

Unit tests: What does unittest.TestCase provide and how can you run selected tests?

a)

TestCase provides assertions and setup/teardown hooks; run selected tests with python -m unittest test_module.TestClass.test_method

b)

TestCase is only a data container; tests run via python -m pytest -k

c)

TestCase requires a main guard to run; selection is impossible

d)

TestCase provides file I/O helpers; run with python tests.py only

49.

Main guard: Why use if __name__ == "__main__": and how does it help testing or modules?

a)

It prevents code from running on import so modules can be reused; it provides an entry point for CLI/test harnesses

b)

It forces code to run twice for debugging

c)

It is required for every function definition

d)

It only affects class methods, not modules

50.

Imports: How do relative imports work inside packages and when should they be avoided?

a)

Use leading dots (from .subpkg import mod) for intra‑package imports; avoid relative imports in scripts executed as the main module and prefer absolute imports to reduce ambiguity

b)

Relative imports are faster and always preferred

c)

Relative imports require setting PYTHONPATH manually

d)

Relative imports work only with namespace packages and should always be avoided