Loading...
Loading...
Python interview guide
Questions from the interview-preparation section: core Python, OOP, collections and output prediction. Every answer is short enough to say aloud, and every question links to the lesson that teaches it.
60 questionsLast updated
It gives me a correct base quickly, and it lets me find the waste in it. For a duplicate check, two loops work first, and then I say that a set removes the second loop, trading memory for time.
Follow-up: How do you state its cost? In plain words with the symbol: the nested loops compare every pair, so O(n squared) time and O(1) extra space.
Common mistake: Jumping straight to a clever idea that I cannot explain. A simple correct answer, then an improvement, is safer.
Lesson: Python Interview Rounds and What Interviewers CheckI restate the problem in my own words, ask about the input, output and limits, and work one small example by hand. Only then do I say my approach and start to code.
Follow-up: Why not start coding at once? Because a wrong assumption costs more time later. A minute of questions often saves ten minutes of rewriting.
Common mistake: Starting to type before the problem is clear. The interviewer then sees a guess, not a plan.
Lesson: Thinking Aloud, Clarifying and Writing Clean PythonI say where I am stuck and what I have tried, go back to a small example, and try the plain approach again. The interviewer often gives a hint when they can follow my thinking.
Common mistake: Going silent. A quiet minute tells the interviewer nothing and removes any chance of a hint.
Lesson: Thinking Aloud, Clarifying and Writing Clean PythonBoth take O(n log n) time. sorted returns a new list and needs extra space, while sort changes the list in place and returns None.
I restate it, ask about empty input, repeats and limits, and work one small example by hand. Then I say a plain solution, improve it and state the cost, write clear code, and finish by tracing a small input and testing edge cases.
Follow-up: What if the plain solution is already fast enough? Then I say so and test it. A correct, clear answer is better than a clever one I cannot explain.
Lesson: How Python Interviews Work CheckpointIn plain words with the symbol: how the time grows with the input and how much extra space is used. For example: "One pass with a set: O(n) time, O(n) extra space; the two-loop version was O(n squared) time and O(1) space."
Lesson: How Python Interviews Work CheckpointCPython is the reference implementation, written in C, and the one you get from python.org. Others include PyPy (faster for long loops), Jython and MicroPython. Behaviour that depends on CPython, such as small-int caching, is not part of the language rules.
Lesson: Python Basics and How It Runs: Interview QuestionsNo. Hints like def f(x: int) -> str are ignored when the program runs. Tools such as mypy or pyright read them to find mistakes before running, which is optional checking, not enforcement.
Indentation is the syntax that marks blocks, in place of braces. A wrong indent changes what the code means or raises IndentationError. Use four spaces and never mix tabs with spaces.
None, False, zero of any number type (0, 0.0), and empty containers or strings ("", [], (), {}, set(), range(0)). Everything else is truthy by default. Custom classes can define __bool__ or __len__ to decide.
The Global Interpreter Lock lets only one thread run Python bytecode at a time in standard CPython. Threads still help for waiting on files or networks, but not for CPU-bound work, where you use multiprocessing. An optional free-threaded build exists in recent versions, but it is not the default.
The REPL is the interactive prompt you get by typing python. It reads a line, evaluates it, prints the result and repeats. It is useful for trying a small idea quickly, such as checking what "abc"[::-1] returns.
is and ==?== compares values by calling __eq__. is compares identity: whether both names point to the same object, like comparing id() values. Use is for None, True and False; use == for everything else.
A shallow copy creates a new outer container but reuses the inner objects. A deep copy recursively copies the inner objects as well. For a flat list of numbers they behave alike; for nested lists, only the deep copy is fully independent.
Lesson: Mutability, References and Copying: Interview QuestionsNeither exactly; it is pass by object reference (also called pass by assignment). The parameter is a new name bound to the caller's object. Mutating that object is visible to the caller, but rebinding the parameter to something else is not.
Lesson: Mutability, References and Copying: Interview Questionst = (1, 2); t += (3,)?A tuple cannot grow, so Python builds a new tuple (1, 2, 3) and rebinds t to it. Other names that pointed to the old tuple are unchanged. For a list, += calls extend and changes the same object.
CPython counts references to each object and frees it when the count reaches zero. A separate cycle collector finds groups of objects that refer only to each other. del name only removes one reference.
Convert the list to a tuple with tuple(items) and the set to frozenset(items). Both are hashable if their items are, so they can be dict keys. They stop other code from changing the data by accident.
* or ** do at the place of a call?f(*items) unpacks an iterable into positional arguments, and f(**options) unpacks a dict into keyword arguments. It is the reverse of collecting them in the definition. The dict keys must be strings that match parameter names.
global and nonlocal?global x makes assignments to x change the module-level variable. nonlocal x makes them change x in the nearest enclosing function. Without either, assigning to x creates a local variable.
An inner function or lambda looks up a variable when it is called, not when it was created. In a loop, all lambdas then see the last value. Bind the value early with a default argument (lambda i=i: i) or functools.partial.
A decorator is a function that takes a function and returns a new one, usually a wrapper that adds behaviour. @log_call above a def is the same as f = log_call(f). Use functools.wraps so the wrapper keeps the original name and docstring.
Use *args and check len(args), or give a parameter a default of None and test is None. Do not use 0 or "" as the missing marker, because those may be valid values. Prefer one clear signature to many cases.
functools.partial do?It creates a new function with some arguments already fixed. partial(power, exponent=2) is a function that always squares. It is a clean alternative to a lambda for binding values early.
except: a bad idea?It catches everything, including KeyboardInterrupt and SystemExit, so Ctrl+C no longer works and real bugs are hidden. Catch the specific error you expect. At most use except Exception at a program boundary, and log it.
Extend Exception: class InsufficientFunds(Exception): pass. A named error lets callers catch your case without catching unrelated ones, and its name explains the problem. Put related errors under one base class of your own.
"r" read (default), "w" write and erase, "a" append, "x" create and fail if it exists, and a "b" suffix for bytes, such as "rb". Add "+" for read and write. In text mode always pass encoding="utf-8".
read(), readline() and readlines()?read() returns the rest of the file as one string. readline() returns one line. readlines() returns a list of all lines, with the newline characters kept. Iterating over the file is the usual choice.
A traceback lists the calls that led to the error, with the most recent call last. Read the last line first: it names the error and message. Then follow the lines up to find which of your lines started it.
Lesson: Exceptions and Files: Interview Questionspython script.py.CPython reads the file, compiles it to bytecode and runs that on its virtual machine. Imported modules are compiled once and cached as .pyc files. Names are looked up at run time, so a typo shows up as a NameError only when that line runs.
A generator produces values one at a time when asked, so it uses almost no memory and can be endless. A list holds all values at once. A generator can be used only once, and (x for x in data) or yield creates one.
sort() and sorted()?list.sort() sorts the list in place and returns None. sorted(iterable) returns a new list and leaves the original alone, and works on any iterable. Both are stable and accept key= and reverse=.
* do inside a list or call, like [*a, *b]?It unpacks an iterable in place. [*a, *b] joins two lists into a new one, and f(*args) spreads items into arguments. Python 3.5+ also allows {**d1, **d2} to merge dicts.
format and %?An f-string, f"{name}: {price:.2f}", evaluates expressions in place and is the clearest and fastest choice. str.format and % do the same job with separate arguments. Use f-strings unless you need to build the template separately.
Strengths: short readable code, a huge library, and quick work in data, scripting and web. Weaknesses: slower than compiled languages for CPU-heavy loops and limited thread parallelism because of the GIL. Name a fix for each, such as NumPy or multiprocessing.
self and why is it explicit in Python?self is the first parameter of an instance method, and it receives the object the method was called on. Python makes it explicit so there is no hidden name: a.deposit(50) is just Account.deposit(a, 50). It also makes it clear which names are attributes and which are local variables.
Follow-up: Is self a keyword? No, it is only a convention. Any name would work, but you should always use self.
Common mistake: Saying self is a keyword like this elsewhere. It is only a normal parameter name that Python fills in.
obj.name = value and name is a class attribute?Python creates a new instance attribute with that name and leaves the class attribute alone. The instance now shadows the class value. Other instances still see the class value.
Lesson: Classes, Instances, self and Attributesself.count += 1 do when count is only a class attribute?It reads the class value, adds one, then assigns the result to the instance. That creates an instance attribute and the class counter does not change. Use ClassName.count += 1 to update the shared value.
Yes. Python objects are open by default, so a.nickname = "Ash" works. This is flexible but it hides typos, so define all attributes in __init__. __slots__ can forbid extra attributes.
An instance method receives self and works on one object. A classmethod receives cls and works on the class, often as an alternative constructor. A staticmethod receives neither and is just a function placed in the class namespace.
__init__?Readers can see the full shape of the object in one place, and methods never hit a missing attribute. Setting attributes elsewhere leads to AttributeError that depends on call order. Linters also warn about it.
Inheritance lets a subclass reuse the attributes and methods of a parent class and add or change only what differs. It removes duplicate code for a true is-a relationship, like Savings is an Account. Use it only when the child can stand in for the parent everywhere.
__init__ and does not call super().__init__()?The parent's __init__ does not run, so the attributes it sets are missing. Later access raises AttributeError. Call super().__init__(...) first unless you want to replace the setup on purpose.
Yes, class D(B, C). It works well for mixins, small classes that add one behaviour. For large trees it gets confusing, so prefer composition unless you really need several parents.
EAFP means try the action and handle the exception. LBYL, look before you leap, means check with hasattr or isinstance first. EAFP is the usual Python style, because it avoids races between the check and the use.
You can write B.method(self) directly, but that bypasses the MRO and may run a shared parent twice. The better design is to have every class call super() so the MRO runs each once.
object do as the base class?Every class inherits from object in Python 3, even if you do not write it. It provides __init__, __repr__, __eq__ and __hash__ defaults. It is always the last entry of the MRO.
__init_subclass__ do?It is a hook that runs on the parent whenever a class inherits from it. Plugin systems use it to register every subclass automatically. It removes the need for a metaclass in simple cases.
Lesson: Inheritance, MRO, super() and Duck TypingIt marks a name as internal, such as _balance or _helper(). from module import * skips such names. It does not block access, so it is a request to other developers.
A property turns a method into something that is read and written like an attribute, using @property and @name.setter. It lets you add validation or computed values without breaking callers. Start with a plain attribute and move to a property only when you need logic.
A class that inherits ABC and has at least one @abstractmethod. It cannot be instantiated, and each subclass must implement every abstract method. It defines a contract and can also hold shared concrete code.
Not as a keyword. You use abstract base classes with only abstract methods, or protocols, or simply duck typing. Many Python programs rely on documentation and duck typing alone.
Lesson: Encapsulation, Properties and Abstract Classestyping.overload then?It is only for type checkers: it describes several signatures for one function so tools understand the return types. The real implementation is a single function that handles all cases. At runtime the decorated stubs do nothing.
Lesson: Encapsulation, Properties and Abstract ClassesNotImplementedError versus an abstract method?Raising NotImplementedError in a base method fails only when that method is called. An @abstractmethod in an ABC fails earlier, when the object is created. Prefer abstract methods for required parts of a contract.
__slots__ have to do with encapsulation?It restricts the instance to a fixed set of attribute names, so typos raise an error instead of creating new attributes. It is a memory saving tool first and a guard second. It does not make attributes private.
Lesson: Encapsulation, Properties and Abstract Classes__eq__ and __hash__?Objects that compare equal must have equal hashes. The hash should use only fields that cannot change while the object is in a set or dict. Mutable objects are normally not hashable.
Lesson: Dunder Methods, Dataclasses and Design QuestionsAn iterable can give you an iterator through __iter__, like a list. An iterator has __next__ and remembers its position, and is used up once consumed. A for loop calls iter() on the iterable and then next() on the result.
NotImplemented?Dunders such as __add__ let + work for your class. When the other operand is a type you do not support, return NotImplemented instead of raising, so Python can try the other side's reflected method. If both decline, it raises TypeError.
frozen=True do in a dataclass?It blocks assignment after creation by raising FrozenInstanceError, and it makes the generated __hash__ work. Frozen dataclasses suit values like points or money. Use dataclasses.replace to make a changed copy.
namedtuple versus a dict?A namedtuple is an immutable tuple with names and works with unpacking. A dataclass is a normal class, mutable by default, with methods and defaults. A dict has no fixed shape, so typos in keys become runtime bugs.
Each lesson has quizzes, practice and more interview questions on the same topic.
Open the first lesson