[{"data":1,"prerenderedAt":152},["ShallowReactive",2],{"topic-python-idioms":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fpython.yml","Python interview questions across language fundamentals, data structures and common gotchas for backend, data and automation roles.","yml","python",{},"Python",3,"frameworks\u002Fpython",1,"QsijsotyAr-3rnhJDWWZmc7hE6HAhylS5t1dKpigMOA",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fpython-idioms.yml","EAFP vs LBYL, PEP 8 style, and the common gotchas and anti-patterns that separate idiomatic Python from translated code.",{},"Pythonic Idioms",14,"idioms","topics\u002Fpython-idioms","WsKznDcUUGEslzKQaB4t9l0sMLRgYpcH3joEEaq7zYA",[25,70,109],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"related":63,"seo":64,"seoDescription":65,"stem":66,"subtopic":67,"topic":19,"topicSlug":21,"updated":68,"__hash__":69},"qa\u002Fpython\u002Fidioms\u002Feafp-lbyl.md","Eafp Lbyl",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fpython\u002Fidioms\u002Feafp-lbyl",[41,46,50,55,59],{"id":42,"difficulty":43,"q":44,"a":45},"eafp-lbyl-meaning","easy","What do EAFP and LBYL mean?","**EAFP** = \"**Easier to Ask Forgiveness than Permission**\": just **attempt**\nthe operation and handle the exception if it fails. **LBYL** = \"**Look Before\nYou Leap**\": **check** the preconditions first, then act only if the checks\npass. They are two styles for handling things that *might* go wrong.\n\n```python\n# LBYL — check first\nif \"key\" in config:\n    value = config[\"key\"]\n\n# EAFP — try and handle failure\ntry:\n    value = config[\"key\"]\nexcept KeyError:\n    value = default\n```\n\nEAFP is the **Pythonic** default — it reads naturally and avoids redundant\nchecks. Rule of thumb: in Python, **try the operation and catch the specific\nexception** rather than pre-validating every condition.\n",{"id":47,"difficulty":35,"q":48,"a":49},"why-eafp-preferred","Why is EAFP preferred in Python?","EAFP fits Python's design: exceptions are **cheap** and **idiomatic**, and the\nstyle avoids **duplicating logic**. With LBYL you often check a condition and\nthen perform the same lookup\u002Foperation again, doing the work twice. EAFP also\nstays correct when an object merely **behaves like** the expected type\n(duck typing) rather than passing an explicit type check.\n\n```python\n# LBYL duplicates the access and breaks on duck-typed objects\nif hasattr(obj, \"read\") and callable(obj.read):\n    data = obj.read()\n\n# EAFP — just use it; let the exception surface real problems\ntry:\n    data = obj.read()\nexcept AttributeError:\n    data = None\n```\n\nThe happy path stays uncluttered and the error handling is explicit. Rule of\nthumb: write the **common case** as straight-line code and catch the\n**specific** exception for the rare failure.\n",{"id":51,"difficulty":52,"q":53,"a":54},"lbyl-race-condition","hard","What race condition does LBYL invite?","LBYL introduces a **TOCTOU** bug — \"**Time Of Check to Time Of Use**\". Between\nthe moment you **check** a condition and the moment you **act** on it, another\nthread or process can change the state, so the check is **stale** and the\naction fails or corrupts data. EAFP avoids the gap by acting atomically and\nhandling failure.\n\n```python\nimport os\n# LBYL — file can be deleted between the check and the open (race!)\nif os.path.exists(path):\n    with open(path) as f:    # may still raise FileNotFoundError\n        data = f.read()\n\n# EAFP — no window; the open either works or raises\ntry:\n    with open(path) as f:\n        data = f.read()\nexcept FileNotFoundError:\n    data = None\n```\n\nThe check-then-act pattern is unsafe in concurrent or filesystem contexts.\nRule of thumb: for files, sockets, and shared state, **attempt the operation**\nand handle the exception rather than checking first.\n",{"id":56,"difficulty":35,"q":57,"a":58},"dict-get-vs-check","When should you use dict.get versus checking for a key?","`dict.get(key, default)` returns the value if present and the **default**\n(`None` if unspecified) otherwise — a clean one-liner that avoids both the\n`if key in d` check **and** a `try\u002Fexcept KeyError`. Use a membership check or\n`try\u002Fexcept` only when you must **distinguish a missing key from a stored\n`None`**, or run different logic in each branch.\n\n```python\ncounts = {\"a\": 1}\ncounts.get(\"b\")          # None — no KeyError\ncounts.get(\"b\", 0)       # 0    — supply a default\n\n# need to tell \"missing\" from \"stored None\"? then check explicitly\nif \"b\" in counts:\n    ...\n# or accumulate with setdefault \u002F defaultdict\ncounts.setdefault(\"c\", 0)\n```\n\nFor counting\u002Fgrouping, `collections.defaultdict` or `Counter` is even cleaner.\nRule of thumb: reach for `.get()` with a default for \"value or fallback\", and\nonly branch explicitly when missing-ness itself is meaningful.\n",{"id":60,"difficulty":52,"q":61,"a":62},"eafp-pitfalls","What are the pitfalls of EAFP, and how do you avoid them?","EAFP done carelessly causes two problems. First, a **too-broad `except`**\n(bare `except:` or `except Exception`) can **swallow unrelated bugs** —\ncatching a `KeyError` you didn't intend, or hiding a `NameError`. Second, the\n`try` block should wrap **only** the line that can fail, so you don't\naccidentally catch exceptions from surrounding code.\n\n```python\n# Bad — hides real errors and over-wide try block\ntry:\n    value = config[\"key\"]\n    result = expensive_call(value)   # its errors get caught too!\nexcept Exception:\n    value = default\n\n# Good — narrow exception, minimal try body\ntry:\n    value = config[\"key\"]\nexcept KeyError:\n    value = default\nresult = expensive_call(value)       # outside the try\n```\n\nAlways catch the **most specific** exception and keep the `try` body small.\nRule of thumb: EAFP means \"catch the *one* expected failure\", never \"catch\neverything and hope\".\n",null,{"description":32},"Python interview questions on EAFP vs LBYL, why try\u002Fexcept is preferred, the race conditions LBYL invites, and dict.get versus checking for a key.","python\u002Fidioms\u002Feafp-lbyl","EAFP vs LBYL","2026-06-18","bCZ-XKDsdon8EdwPgbyY1qZW_3AfSyoMZOhOsdPFSIw",{"id":71,"title":72,"body":73,"description":32,"difficulty":43,"extension":36,"framework":10,"frameworkSlug":8,"meta":77,"navigation":38,"order":33,"path":78,"questions":79,"related":63,"seo":104,"seoDescription":105,"stem":106,"subtopic":107,"topic":19,"topicSlug":21,"updated":68,"__hash__":108},"qa\u002Fpython\u002Fidioms\u002Fpep8-style.md","Pep8 Style",{"type":29,"value":74,"toc":75},[],{"title":32,"searchDepth":33,"depth":33,"links":76},[],{},"\u002Fpython\u002Fidioms\u002Fpep8-style",[80,84,88,92,96,100],{"id":81,"difficulty":43,"q":82,"a":83},"what-is-pep8","What is PEP 8?","**PEP 8** is the official **style guide for Python code** — a set of conventions for\nformatting and naming that make code **consistent and readable** across the\ncommunity. It's a *recommendation*, not a language rule: code that violates PEP 8\nstill runs fine, but consistency aids collaboration.\n\n```python\n# PEP 8 style\ndef calculate_total(items, tax_rate=0.0):\n    subtotal = sum(items)\n    return subtotal * (1 + tax_rate)\n\n# not PEP 8 — cramped, inconsistent spacing\u002Fnaming\ndef calcTotal(items,taxRate=0.0):\n    subTotal=sum(items);return subTotal*(1+taxRate)\n```\n\nPEP 8 covers indentation (4 spaces), naming, whitespace, imports, and line length.\nIts guiding principle: **readability counts**, since code is read far more often than\nit's written.\n",{"id":85,"difficulty":43,"q":86,"a":87},"naming-conventions","What are PEP 8's naming conventions?","PEP 8 assigns a distinct case to each kind of name so readers can tell them apart at\na glance. **`snake_case`** for functions, variables, and modules;\n**`PascalCase`** (CapWords) for classes; **`UPPER_SNAKE_CASE`** for constants. A\nsingle leading underscore signals \"internal\".\n\n```python\nMAX_RETRIES = 3                  # constant — UPPER_SNAKE_CASE\n\nclass HttpClient:                # class — PascalCase\n    def send_request(self):      # method — snake_case\n        retry_count = 0          # variable — snake_case\n        self._session = None     # _leading underscore = \"internal\"\n```\n\nAvoid single-character names like `l`, `O`, `I` (they look like digits). Method\u002F\nfunction names use the same `snake_case` as variables; only classes and exceptions\nuse `PascalCase`.\n",{"id":89,"difficulty":43,"q":90,"a":91},"imports-line-length","What does PEP 8 say about imports and line length?","**Imports** go at the **top** of the file, **one per line**, grouped in order:\nstandard library, third-party, then local — separated by blank lines. For **line\nlength**, PEP 8 recommends a maximum of **79 characters** (72 for docstrings\u002F\ncomments), though many modern teams relax this to 88 or 100.\n\n```python\n# standard library\nimport os\nimport sys\n\n# third-party\nimport requests\n\n# local\nfrom myapp.utils import helper\n\n# avoid: import os, sys   (multiple on one line)\n```\n\nKeeping imports sorted and grouped makes dependencies obvious. The line-length cap\nkeeps code readable in side-by-side diffs and narrow editors; tools like `isort`\nautomate import ordering.\n",{"id":93,"difficulty":43,"q":94,"a":95},"zen-of-python","What is the Zen of Python?","The **Zen of Python** (PEP 20) is a collection of **19 guiding aphorisms** that\ncapture Python's design philosophy. You can read it any time by running\n**`import this`**. It informs *why* the language and its idioms look the way they do.\n\n```python\nimport this\n# Beautiful is better than ugly.\n# Explicit is better than implicit.\n# Simple is better than complex.\n# Readability counts.\n# There should be one-- and preferably only one --obvious way to do it.\n# ...and 14 more\n```\n\nThe aphorisms favor **clarity, simplicity, and explicitness** over cleverness. They\naren't enforced rules but a cultural compass — when two approaches compete, the Zen\nusually points to the more Pythonic one.\n",{"id":97,"difficulty":43,"q":98,"a":99},"formatters-linters","What are black and ruff, and how do formatters differ from linters?","A **formatter** automatically **rewrites** your code into a consistent layout; a\n**linter** **analyzes** code and **reports** style violations and likely bugs without\n(usually) changing it. **black** is the dominant formatter (opinionated,\nnear-zero-config); **ruff** is an extremely fast linter (and formatter) that\nconsolidates many older tools.\n\n```python\n# before black\nx = {'a':1,'b':2}\n# after black\nx = {\"a\": 1, \"b\": 2}\n\n# command line\n# black .          -> reformats files in place\n# ruff check .     -> reports lint issues\n# ruff check --fix -> auto-fixes what it can\n```\n\nRunning a formatter ends style arguments in code review (the tool decides), while a\nlinter catches unused imports, undefined names, and anti-patterns. Most teams run\nboth, often automatically via pre-commit hooks or CI.\n",{"id":101,"difficulty":35,"q":102,"a":103},"when-break-pep8","When is it acceptable to break PEP 8?","PEP 8 itself says **\"A Foolish Consistency is the Hobgoblin of Little Minds\"** —\nstyle serves readability, so break the rules when following them would make code\n**less readable** or when you must stay consistent with **surrounding code** or an\nexisting API.\n\n```python\n# matching an external library's camelCase API\ndef setUp(self):           # unittest requires this exact name\n    ...\n\n# aligning related assignments can aid readability in some cases\nx      = 1\nlonger = 2\n```\n\nLegitimate reasons: compatibility with code that predates PEP 8, conforming to a\nframework's required names, or when a rule genuinely hurts clarity in context. The\nrule of thumb: **deviate only with a clear readability or compatibility\njustification**, not out of laziness.\n",{"description":32},"Python interview questions on PEP 8, naming conventions, imports and line length, the Zen of Python, formatters and linters like black and ruff, and when it is acceptable to break PEP 8.","python\u002Fidioms\u002Fpep8-style","PEP 8 & Style","XZbVx5iojwhWxTkdsYPJmC4TKVdwn0NbvswTvPh9UZ0",{"id":110,"title":111,"body":112,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":116,"navigation":38,"order":11,"path":117,"questions":118,"related":63,"seo":147,"seoDescription":148,"stem":149,"subtopic":150,"topic":19,"topicSlug":21,"updated":68,"__hash__":151},"qa\u002Fpython\u002Fidioms\u002Fgotchas.md","Gotchas",{"type":29,"value":113,"toc":114},[],{"title":32,"searchDepth":33,"depth":33,"links":115},[],{},"\u002Fpython\u002Fidioms\u002Fgotchas",[119,123,127,131,135,139,143],{"id":120,"difficulty":52,"q":121,"a":122},"mutable-default-arg","Why is a mutable default argument a gotcha?","A default argument is **evaluated once when the function is defined**, not on each\ncall. So a mutable default like `[]` or `{}` is **created a single time and shared**\nacross every call that doesn't override it — state leaks between calls.\n\n```python\ndef append_to(item, target=[]):   # the SAME list every call\n    target.append(item)\n    return target\n\nappend_to(1)   # [1]\nappend_to(2)   # [1, 2]  \u003C- surprise!\n\ndef append_to(item, target=None): # the fix: None sentinel\n    if target is None:\n        target = []               # fresh list per call\n    target.append(item)\n    return target\n```\n\nUse **`None` as the default** and create the real object inside the body. This is the\nsingle most famous Python footgun.\n",{"id":124,"difficulty":52,"q":125,"a":126},"late-binding-closures","What is the late-binding closure gotcha in loops?","Closures in Python capture **variables by reference, not by value**. When you create\nfunctions in a loop, they all close over the **same** loop variable, which holds its\n**final value** by the time they're called.\n\n```python\nfuncs = [lambda: i for i in range(3)]\n[f() for f in funcs]          # [2, 2, 2]  — not [0, 1, 2]!\n\n# fix: bind the current value via a default argument\nfuncs = [lambda i=i: i for i in range(3)]\n[f() for f in funcs]          # [0, 1, 2]\n```\n\nThe default-argument trick captures `i`'s value **at definition time**. (`functools.\npartial` works too.) Remember: closures see the variable's *latest* value, not a\nsnapshot.\n",{"id":128,"difficulty":35,"q":129,"a":130},"modify-while-iterating","What goes wrong when you modify a list while iterating it?","Changing a list's **size during iteration** shifts the internal index, causing\nelements to be **skipped or repeated**. With dicts and sets it's worse — Python\nraises `RuntimeError: dictionary changed size during iteration`.\n\n```python\nnums = [1, 2, 3, 4]\nfor n in nums:\n    if n % 2 == 0:\n        nums.remove(n)        # skips elements — buggy\nprint(nums)                   # [1, 3] for some inputs, wrong for others\n\nnums = [n for n in nums if n % 2]   # build a new list instead\n```\n\nFix it by iterating over a **copy** (`for n in nums[:]`) or, better, building a new\ncollection with a comprehension or `filter`. Never mutate a container's size while\nlooping over it.\n",{"id":132,"difficulty":35,"q":133,"a":134},"is-vs-eq-cached","Why does `is` give surprising results on numbers and strings?","`is` tests **identity** (same object), while `==` tests **value**. CPython **caches**\nsmall integers (−5 to 256) and **interns** some strings, so `is` *coincidentally*\nreturns `True` for those — but fails for values outside the cache, making it look\nunreliable.\n\n```python\na = 256; b = 256\na is b          # True  — cached\n\nc = 1000; d = 1000\nc is d          # often False — not cached\nc == d          # True   — always correct\n\nx = \"hi\"; y = \"hi\"\nx is y          # True (interned) — don't rely on it\n```\n\nCaching is an **implementation detail**, not a guarantee. Rule: use `==` for value\ncomparison; reserve `is` for singletons (`None`, `True`, `False`).\n",{"id":136,"difficulty":35,"q":137,"a":138},"bare-except","Why is a bare except an anti-pattern?","A bare **`except:`** (or `except Exception:` used carelessly) catches **everything** —\nincluding `KeyboardInterrupt` and `SystemExit` — and **swallows the error silently**,\nhiding bugs and making programs impossible to interrupt or debug.\n\n```python\ntry:\n    risky()\nexcept:                  # catches EVERYTHING, even Ctrl-C\n    pass                 # error vanishes — undebuggable\n\ntry:\n    risky()\nexcept ValueError as e:  # catch only what you expect\n    log.error(\"bad value: %s\", e)\n    raise                # or handle it deliberately\n```\n\nCatch the **specific exceptions** you can actually handle, and avoid `pass` in an\n`except` (at minimum log it). If you must catch broadly, use `except Exception` (not\nbare) so system-exiting signals still propagate.\n",{"id":140,"difficulty":52,"q":141,"a":142},"mutable-class-attr","Why is a mutable class attribute a common bug?","A mutable value assigned **at class level** (outside `__init__`) is **one object\nshared by every instance**. Mutating it through any instance affects **all** of them\n— usually not what you intend.\n\n```python\nclass Cart:\n    items = []                  # SHARED across all instances\n    def add(self, x):\n        self.items.append(x)\n\na, b = Cart(), Cart()\na.add(\"apple\")\nb.items                         # ['apple'] — leaked into b!\n\nclass Cart:\n    def __init__(self):\n        self.items = []         # per-instance — correct\n```\n\nInitialize mutable attributes **inside `__init__`** so each instance gets its own.\nClass-level attributes are fine for **immutable** constants\u002Fdefaults, but never for\nmutable per-instance state.\n",{"id":144,"difficulty":43,"q":145,"a":146},"shadowing-builtins","What is the problem with shadowing builtins?","Naming a variable after a builtin — `list`, `dict`, `id`, `str`, `type`, `sum` —\n**shadows** it in that scope, so the original becomes unusable and you get confusing\nerrors later when you try to call it.\n\n```python\nlist = [1, 2, 3]          # shadows the built-in list type\nother = list((4, 5))      # TypeError: 'list' object is not callable\n\nid = 42                   # now id() is gone\nid(other)                 # TypeError: 'int' object is not callable\n```\n\nPick non-conflicting names: `items`\u002F`values` instead of `list`, `mapping` instead of\n`dict`, `user_id` instead of `id`. Linters flag builtin shadowing — heed the warning\nto avoid these baffling bugs.\n",{"description":32},"Python interview questions on common gotchas: mutable default arguments, late-binding closures, modifying a list while iterating, is vs ==, bare except, mutable class attributes, and shadowing builtins.","python\u002Fidioms\u002Fgotchas","Common Gotchas & Anti-patterns","dQ6TWsvM0wnn7CS37n3Xav6bFViKq0g1Er9l_hofq4c",1781808675738]