›What problem does a virtual environment solve?
Without one, every project on a machine shares the same global set of installed packages — two projects needing
different versions of the same library can't coexist, and installing a dependency for one project can silently
break another. A venv gives each project its own isolated package directory and its own pip/python binaries.
›Why is subprocess.run(cmd, shell=True) risky?
With shell=True, the command string is handed to a real shell for interpretation — if any part of it is built
from external input (a CLI argument, an API payload, an environment variable), an attacker can inject shell
metacharacters to run arbitrary commands. Passing a list of arguments instead never invokes a shell, so there's
nothing to inject into.
›What's the difference between a list and a tuple?
Both are ordered collections. A list is mutable — items can be added, removed or changed after creation. A tuple is immutable — its contents are fixed once created, which makes it hashable (usable as a dict key or set member) and signals intent that the collection shouldn't change.
›What's the difference between args and kwargs (*args, **kwargs)?
*args collects any extra positional arguments into a tuple; **kwargs collects any extra keyword arguments into
a dict. They let a function accept a variable, unspecified number of arguments — common in wrapper/decorator
functions that need to pass whatever they're given through to another function unchanged.
›What is the GIL, and why does it matter for concurrency?
The Global Interpreter Lock ensures only one thread executes Python bytecode at a time in CPython, even on a
multi-core machine. That makes threading a poor fit for CPU-bound work (it won't actually parallelize across
cores), but threading still helps for I/O-bound work (network calls, disk I/O) since the GIL is released during
blocking I/O. For genuine CPU parallelism, use multiple processes (multiprocessing) instead.
›What's the difference between == and is?
== compares value equality — do these two objects represent the same value. is compares identity — are these
two names literally bound to the same object in memory. Two equal-looking lists are == but not is; comparing
against None should always use is None, since None is a singleton and identity is both correct and faster.
›What's the difference between a script argument sourced from argparse versus os.environ?
A CLI argument (argparse) is explicit at invocation time and shows up in shell history and process listings — fine
for non-sensitive config like --env staging. An environment variable is set in the process's environment rather
than the command line, which keeps it out of ps output and shell history — the more common choice for secrets
like API keys, though neither is a substitute for a real secrets manager in production.
›What does if __name__ == "__main__": do?
__name__ is "__main__" only when a file is run directly, and is the module's actual name when it's imported by
something else. This guard lets a file define reusable functions/classes that other code can import without also
re-running the file's top-level script logic as a side effect of importing it.
›How would you handle a missing required environment variable?
os.environ["KEY"] raises KeyError immediately if it's unset — fail loudly at startup rather than limping along
with a None that causes a confusing error much later. Use os.environ.get("KEY", default) only for genuinely
optional configuration with a sane default.