Príručka Pythonu
Syntax, príklady a stručné vysvetlenia — od základov jazyka po algoritmické vzory, triedy a testy.
Základy jazyka13
n = 42
ratio = 1.5
name = "Ada"
flag = True
nothing = NoneThe type comes from the value; no declaration needed. Check with type(x) or isinstance(x, int).
f"{name} solved {n} tasks"
f"{ratio:.2f}"
f"{n:>5}"
f"{n=}"Interpolate expressions into a string. :.2f rounds to two decimals, :>5 pads, {n=} prints name and value.
if items:
...
if not s:
...
bool(0), bool(""), bool([]) # FalseEmpty collections, 0, "" and None are falsy. Prefer `if items` over `if len(items) > 0`.
a, b = b, a
first, *rest = [1, 2, 3, 4]
x = y = 0Swap without a temporary; *rest absorbs the remainder of the sequence.
sign = "+" if n >= 0 else "-"A one-line conditional. It yields a value, so it can be assigned or returned.
7 // 2 # 3
7 % 2 # 1
divmod(7, 2) # (3, 1)
-7 // 2 # -4// floors rather than truncating: -7 // 2 is -4. divmod returns quotient and remainder at once.
len([10, 20, 30]) # 3`len()` is a built-in function that uses the `__len__` protocol, so it works consistently with different containers. Python has no `.len()` method.
print("a", "b", 3, sep="-", end="\n")`print()` accepts multiple values, inserts `sep` between them, and appends `end` after the last one. A space and a newline are used by default.
n = int(input())`input()` always returns a string, even when the user enters a number. `int()` raises `ValueError` when the string is not a valid integer.
s = str(42)
n = int("42")
x = float("3.5")
h = int("ff", 16)`str`, `int`, and `float` create values of the requested type, while the second argument of `int()` specifies the numeral base. An invalid string raises `ValueError`.
isinstance(True, int) # True
type(True) is int # False`isinstance()` respects inheritance and is usually the better way to check a type. `type(x) is T` accepts only an exact type match.
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items`None` is a safe sentinel when a mutable object must be created separately for every call. Check it with `is None`, not `== None`.
a = [1]
b = [1]
a == b # True
a is b # False
x = 1
y = 1
x is y # True`==` compares values, while `is` checks whether two references point to the same object. Small integers may be interned, so `is` must not be used for numeric equality.
Riadenie toku10
if score >= 90:
grade = "A"
elif score >= 75:
grade = "B"
else:
grade = "C"Conditions are checked from top to bottom, and the first matching branch runs. A broad condition can hide a more specific one below it.
a == b
a != b
a < b
a <= b
a is None
0 <= x < n
a < b == c
x not in blockedA comparison chain behaves like multiple checks joined by and, but the middle expression is evaluated once. Compare with None using is or is not.
value = cached or compute()
result = ready and data
flag = not itemsand and or short-circuit and return one of their operands, not necessarily a bool. The not operator always returns a bool.
for item in items:
process(item)
while left < right:
left += 1for iterates over an iterable, while while repeats as long as its condition is truthy. A while loop must change relevant state or it may never terminate.
range(stop)
range(start, stop)
range(start, stop, step)
range(n - 1, -1, -1)range creates a lazy integer sequence and excludes stop. Counting backwards requires a negative step, and a zero step is invalid.
for x in items:
if x == target:
breakbreak immediately exits the nearest loop. In nested loops, the outer loop continues.
for x in items:
if x < 0:
continue
process(x)continue skips the rest of the current iteration and proceeds to the next one. In a while loop, update loop state before continuing.
for x in items:
if x == target:
found = True
break
else:
found = FalseA loop else block runs only when the loop finishes without break. It also runs when the iterable is empty.
match token:
case 0:
kind = "zero"
case int() if token > 0:
kind = "positive"
case _:
kind = "other"match selects the first matching pattern, while _ acts as a catch-all. Execution does not fall through into later cases.
for key, value in pairs:
process(key, value)
for i, (x, y) in enumerate(points):
process(i, x, y)A loop target can unpack nested structures directly. The number and shape of values must match unless a starred target is used.
Funkcie10
def bounds(values):
return min(values), max(values)
low, high = bounds(items)Multiple return values are actually packed into a tuple. When unpacking, the number of targets must match the number of items.
def power(base, exponent=2):
return base ** exponentA default value is used when the argument is omitted. The default expression is evaluated once when the function is created.
def add_item(item, items=[]):
items.append(item)
return itemsA mutable default object is shared across calls, so data can accumulate unexpectedly. Use None and create the object inside the function instead.
def collect(first, *args, **kwargs):
return first, args, kwargs*args collects extra positional arguments into a tuple, while **kwargs collects keyword arguments into a dictionary. Regular parameters are bound first.
def clamp(value, *, low=0, high=100):
return max(low, min(value, high))Parameters after * can only be passed by name. This prevents mistakes caused by the unclear order of similar arguments.
key = lambda item: item[1]
result = sorted(items, key=key)lambda creates a small function from a single expression and cannot contain regular statements. Use def for more complex logic.
def make_adder(delta):
def add(value):
return value + delta
return addA closure keeps access to names from its enclosing function after that function returns. Names are captured by reference, which can cause late-binding surprises in loops.
count = 0
def outer():
total = 0
def update():
global count
nonlocal total
count += 1
total += 1
return total
return updateglobal targets a module-level name, while nonlocal targets a name in the nearest enclosing scope that already binds it. Without these declarations, assignment creates a local name.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)lru_cache stores call results and is especially useful for repeated recursive subproblems. Arguments must be hashable, and maxsize=None creates an unbounded cache.
def binary_search(a: list[int], target: int) -> int | None:
...Annotations describe expected parameter and return types but do not enforce them at runtime by themselves. X | None means the result may be absent.
Reťazce7
s[1:4]
s[:3]
s[-2:]
s[::-1]
s[::2]The right bound is exclusive. s[::-1] reverses the string, s[::2] takes every second character, negative indices count from the end.
"a,b,c".split(",") # ["a", "b", "c"]
"a b c".split()
",".join(["a", "b"]) # "a,b"split() with no argument splits on any whitespace and drops empties. join glues the parts back.
s.strip()
s.lower()
s.upper()
s.replace("a", "b")
s.startswith("ab")
s.endswith(".py")
s.count("a")
s.find("a") # -1Strings are immutable: methods return a new string. find returns -1 when missing; index raises instead.
s.isdigit()
s.isalpha()
s.isalnum()
c.isupper()
c.islower()
"ABC123".isupper() # True
"123".isupper() # False
"ab" in sisdigit/isalpha/isalnum are true only for a non-empty string that matches all the way through. isupper/islower check every cased character: digits and punctuation are ignored, but at least one cased character is required. `in` tests whether a character or substring is present.
sorted(s)
sorted(s1) == sorted(s2)
set("aab") == set("ab") # True
sorted("aab") == sorted("ab") # Falsesorted(s) gives the characters in order, so comparing two of those lists is the usual anagram check. A set will not do: it loses how many times each character occurs. Both forms count case, spaces and punctuation as characters.
parts = []
for w in words:
parts.append(w)
result = "".join(parts)Do not build strings with += in a loop — each step copies everything. Collect into a list and join once.
ord('a') # 97
chr(97) # 'a'
ord(c) - ord('a')Character code and back. ord(c) - ord("a") gives a letter index — the usual trick for a 26-slot counter.
Reťazce: pokročilé10
"{name}: {score:.2f}".format(name="Ada", score=9.876)
score = 9.876
f"{score:.2f}"`str.format` substitutes values into a template, while an f-string evaluates expressions directly in the current scope. Format specifiers control precision, signs and representation, and literal braces are written as `{{` and `}}`.
x = "42"
f"{x:>8}"
f"{x:<8}"
f"{x:*^8}"The `<`, `>` and `^` specifiers align a value to the left, right or centre, and a preceding character defines the fill. The width is a minimum and does not truncate a value that is too long.
table = str.maketrans({"a": "@", "e": "3"})
"peace".translate(table)`str.maketrans` creates a translation table, and `translate` applies it to all characters in one pass. A table value of `None` removes the corresponding character.
head, sep, tail = "key=value".partition("=")`partition` splits only at the first occurrence and always returns three elements, including the separator itself. If the separator is absent, the original string is the first element and the other two are empty.
"a\nb\r\nc".splitlines() # ["a", "b", "c"]`splitlines` recognises multiple line-boundary formats and removes them by default. Unlike `split("\n")`, it correctly handles several standard line separators.
"42".zfill(5) # "00042"
"-42".zfill(5) # "-0042"`zfill` pads a string with leading zeros to a minimum width and places the zeros after a numeric sign. It does not truncate a string that is already wider than the requested width.
"unhappy".removeprefix("un") # "happy"
"report.csv".removesuffix(".csv") # "report"`removeprefix` and `removesuffix` remove one exact match only from the corresponding end of a string. Unlike `strip`, their argument is not interpreted as a set of individual characters.
import re
re.match(r"\d+", text)
re.search(r"\d+", text)
re.findall(r"\d+", text)
re.sub(r"\s+", " ", text)`match` checks only at the start of a string, `search` finds the first match anywhere, `findall` collects all matches, and `sub` replaces them. A repeatedly used pattern should usually be created once with `re.compile`.
data = "café".encode("utf-8")
text = data.decode("utf-8")`encode` converts a `str` to `bytes`, while `decode` performs the reverse conversion using a specified encoding. Using inconsistent encodings can raise an error or corrupt the text.
"Straße".lower() # "straße"
"Straße".casefold() # "strasse"`lower` performs normal lowercase conversion, while `casefold` applies a more aggressive case folding (ß → ss) for caseless comparison. It is not Unicode normalisation: a precomposed `é` and `e` plus a combining accent still compare unequal after `casefold` — that is what `unicodedata.normalize` is for.
Zoznamy a rezy13
a = [0, 1, 2, 3, 4]
a[1:4] # [1, 2, 3]
a[:3]
a[-2:]
a[::-1]
b = a[:]A slice makes a new list. a[:] copies the top level only — nested lists stay shared.
a.append(x) # O(1)
a.extend([x, y])
a.insert(0, x) # O(n)
a.pop() # O(1)
a.pop(0) # O(n)Inserting or removing at the front costs O(n). If you need both ends, use collections.deque.
for i, x in enumerate(a):
...
for i, x in enumerate(a, start=1):
...Index and value together. Replaces for i in range(len(a)) and removes the off-by-one risk.
for x, y in zip(a, b):
...
list(zip(a, b))
zip(*matrix)Walks several sequences in parallel and stops at the shortest one. zip(*matrix) transposes a matrix.
[x for x in a if x > 0]
[x * 2 for x in a]
[y for row in m for y in row]
{x: x ** 2 for x in a}Shorter and faster than a loop with append. Two for clauses flatten a nested list; braces with a colon build a dict. Nested clauses read in the same order as a plain loop.
any(x > 0 for x in a)
all(x > 0 for x in a)
sum(a)
min(a)
max(a)
max(a, key=abs)
max(a, default=0)any/all short-circuit on the first decisive element. key picks what to compare; default avoids an error on an empty list.
grid = [[0] * m for _ in range(n)] # ✓
grid = [[0] * m] * n # ✗The second form is wrong: list multiplication copies the reference, not the contents, so every row becomes the same list. Build with a comprehension.
a = [1, 2, 3]
b = list(range(3))
zeros = [0] * 5
rows = [[0] * 3] * 2A literal or `list()` creates a list, while multiplication repeats its elements. Repeating a nested mutable object copies references, so the rows in `rows` are linked.
a = [4, 2, 4]
i = a.index(4)
n = a.count(4)`index()` returns the position of the first match, while `count()` returns the number of matches. `index()` raises `ValueError` when the value is absent.
a = [10, 20, 30, 20]
a.remove(20)
del a[0]
x = a.pop()`remove()` deletes the first matching value, `del` deletes by index or slice, and `pop()` deletes and returns an element. A missing value for `remove()` or an invalid index raises an exception.
from copy import deepcopy
a = [[1], [2]]
b = a.copy()
c = deepcopy(a)
a[0].append(9)`copy()` creates a new outer list but preserves references to nested objects. `deepcopy()` recursively copies nested content but usually requires more time and memory.
def add(a, b):
return a + b
nums = [2, 3]
result = add(*nums)The `*` operator passes list elements as separate positional arguments. Their count must match the function signature unless it accepts `*args`.
point = (2, 3)
seen = {point}A tuple resembles a list, but its elements cannot be replaced, added, or removed. It can be stored in a set or used as a dictionary key only when all its elements are hashable.
Slovníky a množiny13
d[k] # KeyError
d.get(k) # None
d.get(k, 0)
d.setdefault(k, []).append(x)get never raises. setdefault creates the value when the key is missing and returns it in one step.
for k in d:
...
for k, v in d.items():
...
d.keys()
d.values()Iterating a dict yields keys. Insertion order is preserved since Python 3.7.
from collections import Counter
c = Counter(a)
c[x] # 0
c.most_common(2)
Counter(s1) == Counter(s2)A ready-made frequency counter. A missing key gives 0 rather than an error; most_common returns the top entries.
from collections import defaultdict
g = defaultdict(list)
g[k].append(x)
cnt = defaultdict(int)
cnt[k] += 1The value is created on first access. Handy for grouping and for graph adjacency lists.
s = set(a)
s.add(x)
s.discard(x)
x in s # O(1)
a_set & b_set
a_set | b_set
a_set - b_setMembership is O(1) instead of a list’s O(n). & is intersection, | union, - difference. A set keeps no order and no duplicates.
seen = set()
for x in a:
if x in seen:
return True
seen.add(x)Finds a duplicate in one pass, O(n). The same shape solves two-sum: look for target - x in seen.
d1 = {"a": 1}
d2 = dict(a=1)
d3 = dict([("a", 1)])
d4 = {k: 0 for k in ("a", "b")}A dictionary can be created with a literal, `dict()`, a sequence of pairs, or a dictionary comprehension. Keyword arguments to `dict()` work only for string keys that are valid identifiers.
d = {"a": 1, "c": 3}
"a" in d # True
x = d.pop("a", 0)
y = d.pop("b", 0)
del d["c"]For a dictionary, `in` checks keys rather than values. `del` raises `KeyError` for a missing key, while `pop(key, default)` returns the fallback value.
d = {"a": 1, "b": 2}
keys = d.keys()
values = d.values()
pairs = d.items()
d["c"] = 3
list(keys) # ["a", "b", "c"]The `keys()`, `values()`, and `items()` methods return dynamic views rather than lists. They reflect later dictionary changes; use an explicit `list` conversion for an independent snapshot.
a = {"x": 1}
b = {"x": 2, "y": 3}
c = a | b
a.update(b)The `|` operator creates a new dictionary, while `update()` modifies the existing one. When keys overlap, the value from the right dictionary or supplied source wins.
d = {"a": 3, "b": 1, "c": 2}
pairs = sorted(d.items(), key=lambda item: item[1])To sort by values, sort the pairs from `items()` using their second element as the key. The result is a list of tuples, not an automatically sorted dictionary.
freq = {}
for x in [2, 1, 2]:
freq[x] = freq.get(x, 0) + 1Frequencies can be counted with a regular dictionary by using zero for an unseen key. Without a default value, `get()` returns `None`, which cannot be added to a number.
edge = frozenset({2, 5})
weights = {edge: 7}A `frozenset` is immutable and hashable, so it can be used as a dictionary key or an element of another set. A regular `set` cannot be used this way.
Kolekcie štandardnej knižnice10
from collections import deque
q = deque([2, 3])
q.appendleft(1)
q.append(4)
left = q.popleft()
right = q.pop()deque adds and removes elements from both ends in O(1). For a queue, use append and popleft instead of list pop(0).
from collections import deque
q = deque([1, 2, 3], maxlen=3)
q.append(4)
q.rotate(1)
q.rotate(-1)maxlen automatically discards items from the opposite end when the deque is full. rotate shifts items right for a positive argument and left for a negative one.
import heapq
h = [4, 1, 7, 2]
heapq.heapify(h)
heapq.heappush(h, 0)
smallest = heapq.heappop(h)heapq implements a min-heap, so the smallest item is always at the root. heapify runs in O(n), while push and pop take O(log n).
import heapq
nums = [4, 1, 7, 2]
largest = heapq.nlargest(2, nums)
smallest = heapq.nsmallest(2, nums)
h = [-x for x in nums]
heapq.heapify(h)
maximum = -heapq.heappop(h)nlargest and nsmallest select k extreme items without fully sorting the input. A max-heap is commonly simulated by storing numbers with the opposite sign.
from collections import Counter
a = Counter("banana")
a.update("band")
top = a.most_common(2)
b = Counter("an")
added = a + b
common = a & bCounter stores element frequencies and supports most_common, update, and operations between counters. Arithmetic operations create a new Counter and generally omit zero or negative results.
from collections import defaultdict
groups = defaultdict(list)
counts = defaultdict(int)
seen = defaultdict(set)
groups["a"].append(1)
value = counts["missing"]defaultdict creates a value with its factory when a missing key is accessed. Reading through square brackets mutates the mapping, so use in or get when only checking.
from collections import OrderedDict
d = {"a": 1, "b": 2}
od = OrderedDict(d)
od.move_to_end("a")
first = od.popitem(last=False)Regular dict already preserves insertion order, so OrderedDict is mainly useful for explicit reordering and removal from a chosen end. Equality between two OrderedDict instances also considers item order.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(2, 3)
x = p.xnamedtuple creates a compact immutable type whose fields are accessible by name and index. For richer models with methods and defaults, a dataclass is usually more convenient.
from array import array
nums = array("i", [1, 2, 3])
raw = bytes([65, 66, 67])
mutable = bytearray(raw)
mutable[0] = 90array stores values of one primitive type more compactly than a regular list, but is rarely needed in algorithm problems. bytes is immutable, while bytearray allows individual bytes to be changed.
from collections import ChainMap
defaults = {"timeout": 30}
overrides = {"timeout": 10}
config = ChainMap(overrides, defaults)
value = config["timeout"]ChainMap searches multiple mappings from left to right without copying or merging them. Assignments modify only the first mapping by default.
Iterácia a generátory10
squares_list = [x * x for x in values]
squares_gen = (x * x for x in values)A list comprehension builds the entire list immediately, while a generator expression produces values on demand. A generator saves memory but is normally consumed only once.
def countdown(n):
while n > 0:
yield n
n -= 1Using yield makes a function a generator function: calling it returns a generator, and its body runs during iteration. Execution resumes after each yield from the saved position.
def flatten(chunks):
for chunk in chunks:
yield from chunkyield from forwards items from a nested iterable without a manual inner loop. Each use flattens only one level.
it = iter([10])
first = next(it)
missing = next(it, None)
try:
next(it)
except StopIteration:
...iter obtains an iterator, while next retrieves the next item and advances its state. Supplying a default prevents StopIteration when the iterator is exhausted.
from itertools import count, cycle, islice
numbers = list(islice(count(10, 2), 4))
pattern = list(islice(cycle("ab"), 5))count and cycle create potentially infinite iterators, while islice limits them without materializing the full sequence. islice does not support negative indices or a negative step.
from itertools import chain
merged = list(chain([1, 2], [3, 4]))
flat = list(chain.from_iterable([[1, 2], [3, 4]]))chain lazily iterates through several iterables one after another. chain.from_iterable accepts an outer iterable and flattens one level.
from itertools import combinations, permutations, product
pairs = list(product([0, 1], repeat=2))
orders = list(permutations([1, 2, 3], 2))
choices = list(combinations([1, 2, 3], 2))product builds a Cartesian product, permutations considers order, and combinations does not. They return iterators, but the number of results can grow combinatorially.
from itertools import groupby
items = [1, 1, 2, 2, 2, 3]
groups = [(key, list(group)) for key, group in groupby(items)]groupby groups only consecutive items with the same key. For global grouping, sort the data by the same key first.
a = [1, 2, 3]
it = reversed(a)
b = list(it)reversed returns a reverse iterator and does not modify the original sequence. The object must support reverse iteration or provide indexed access and a length.
total = sum(x * x for x in values)
has_negative = any(x < 0 for x in values)
all_even = all(x % 2 == 0 for x in values)sum consumes every value from the generator, while any and all stop as soon as the result is known. For an empty source, any returns False and all returns True.
Čísla a matematika10
n = 10**100
n.bit_length()The `int` type stores integers of arbitrary size, so there is no fixed-width overflow. Memory and computation time are the practical limits, but since Python 3.11 converting between `int` and `str` is capped at 4300 decimal digits and raises `ValueError` beyond it.
0.1 + 0.2 == 0.3 # False
x = float("inf")The `float` type uses binary approximations, so many decimal fractions cannot be represented exactly. Use `math.isclose` for comparisons and `decimal.Decimal` when exact decimal arithmetic is required.
round(2.5) # 2
round(3.5) # 4
round(12.345, 2)`round` resolves an exact halfway case toward the nearest even value rather than always rounding up. Results for `float` values can also be affected by binary representation error.
abs(-7) # 7
2**10 # 1024
pow(2, 10) # 1024
pow(2, 10, 1000) # 24`abs` returns an absolute value, while `**` and `pow` perform exponentiation. The three-argument `pow(a, b, mod)` efficiently computes a modular power without creating a huge intermediate integer.
from math import sqrt, isqrt, gcd, factorial
sqrt(81) # 9.0
isqrt(80) # 8
gcd(18, 24) # 6
factorial(5) # 120`sqrt` returns a floating-point root, while `isqrt` returns the exact integer floor of the root without `float` errors. `gcd` computes the greatest common divisor, and `factorial` accepts only non-negative integers.
from math import ceil, floor, inf
ceil(2.1) # 3
floor(2.9) # 2
inf > 10**100 # True`ceil` rounds toward positive infinity, while `floor` rounds toward negative infinity, which matters especially for negative values. `math.inf` compares as infinity but is not produced automatically by division by zero.
try:
1 / 0
except ZeroDivisionError:
...Division, floor division and modulo with a zero divisor raise `ZeroDivisionError`. Python does not convert such a division into infinity.
int("101", 2) # 5
int("0xff", 0) # 255
int(3.9) # 3
float("2.5") # 2.5
str(42) # "42"
bin(10) # "0b1010"
hex(255) # "0xff"
oct(8) # "0o10"`int` can parse strings using bases from 2 to 36, while base `0` detects a standard prefix automatically. Converting a `float` to `int` truncates toward zero, and `bin`, `hex` and `oct` return prefixed strings.
x = 0b1100
y = 0b1010
x & y # 8
x | y # 14
x ^ y # 6
~x # -13
x << 2 # 48
x >> 2 # 3
x.bit_count() # 2Bitwise operations work with the binary representation of integers and are commonly used for masks and state sets. Integers have no fixed width, and `~x` is always equal to `-x - 1`.
import random
rng = random.Random(42)
rng.randrange(1, 10)
rng.randint(1, 10)
rng.choice([1, 2, 3])
a = [1, 2, 3]
rng.shuffle(a)`random` generates reproducible pseudo-random values: `randrange` excludes its stop value, while `randint` includes both endpoints. The module is not suitable for passwords or cryptography.
Triedenie a vyhľadávanie6
b = sorted(a)
a.sort()
sorted(a, reverse=True)sorted returns a new list; sort mutates in place and returns None. The sort is stable.
sorted(words, key=len)
sorted(pairs, key=lambda p: p[1])
sorted(a, key=lambda x: (-x[1], x[0]))key is called once per element. A tuple gives multiple levels; a minus sign reverses a numeric field.
a.sort(key=lambda x: x.age)
a.sort(key=lambda x: x.name)Equal elements keep their original order, so you can sort by the least significant key first.
from operator import itemgetter
sorted(rows, key=itemgetter(1, 0))Same as a lambda but faster, and clearer when sorting by several fields.
from bisect import bisect_left, bisect_right, insort
i = bisect_left(a, x)
j = bisect_right(a, x)
insort(a, x)Binary search over a sorted list in O(log n). bisect_left gives the first position of x and bisect_right the one just after it, so j - i is the occurrence count. insort inserts while keeping order.
lo, hi = 0, len(a) - 1
while lo <= hi:
mid = (lo + hi) // 2
if a[mid] == x:
return mid
if a[mid] < x:
lo = mid + 1
else:
hi = mid - 1
return -1The list must be sorted. Move the bounds past mid, or the loop never terminates.
Zásobník, front a dva ukazovatele9
stack = []
stack.append(1)
stack.append(2)
top = stack.pop()A list efficiently implements a LIFO stack with append and pop from the end in amortized O(1). Removing from the front of a list takes O(n).
from collections import deque
q = deque([1, 2])
q.append(3)
first = q.popleft()A FIFO queue is efficiently implemented with deque: append adds on the right and popleft removes on the left in O(1). A list with pop(0) is unsuitable for large queues because elements must be shifted.
def next_greater(nums):
result = [-1] * len(nums)
stack = []
for i, x in enumerate(nums):
while stack and nums[stack[-1]] < x:
result[stack.pop()] = x
stack.append(i)
return resultA monotonic stack keeps values or indices ordered within the stack and finds nearest greater or smaller elements in O(n). Store indices when distances or positions are needed.
def is_balanced(s):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in s:
if ch in "([{":
stack.append(ch)
elif ch in pairs and (not stack or stack.pop() != pairs[ch]):
return False
return not stackOpening brackets are pushed onto a stack, and each closing bracket must match the top. The stack must be empty afterward, or some brackets remain unclosed.
def has_pair(nums, target):
left, right = 0, len(nums) - 1
while left < right:
total = nums[left] + nums[right]
if total == target:
return True
if total < target:
left += 1
else:
right -= 1
return FalsePointers from opposite ends shrink the search space when the current result determines which pointer should move. For pair-sum search, the array must be sorted.
def middle_node(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slowA fast pointer moves quicker than a slow pointer, allowing middle or cycle detection without extra memory. Check both fast and its next node before advancing twice.
def remove_duplicates(nums):
if not nums:
return 0
write = 1
for read in range(1, len(nums)):
if nums[read] != nums[write - 1]:
nums[write] = nums[read]
write += 1
return writeA read pointer scans the array while a write pointer builds the valid prefix without another array. This version assumes equal values are adjacent, usually because the input is sorted.
def reverse_in_place(nums):
left, right = 0, len(nums) - 1
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1Swapping elements from both ends reverses a mutable sequence in O(n) time and O(1) extra space. A string cannot be modified this way because it is immutable.
def merge_sorted(a, b):
i = j = 0
result = []
while i < len(a) and j < len(b):
if a[i] <= b[j]:
result.append(a[i])
i += 1
else:
result.append(b[j])
j += 1
result.extend(a[i:])
result.extend(b[j:])
return resultTwo pointers merge sorted sequences in O(n + m) by repeatedly choosing the smaller current item. After the main loop, append the unprocessed tail.
Klzavé okno a prefixové súčty9
def window_sums(nums, k):
if k > len(nums):
return []
current = sum(nums[:k])
result = [current]
for right in range(k, len(nums)):
current += nums[right] - nums[right - k]
result.append(current)
return resultA fixed window updates its state by adding the entering item and removing the leaving one, processing all windows in O(n). Define behavior explicitly when k is zero or larger than the input.
def min_length(nums, target):
left = 0
total = 0
answer = len(nums) + 1
for right, x in enumerate(nums):
total += x
while total >= target:
answer = min(answer, right - left + 1)
total -= nums[left]
left += 1
return 0 if answer > len(nums) else answerA variable window expands on the right and shrinks from the left while the required condition holds. Sum-based versions usually require nonnegative values, otherwise window monotonicity breaks.
from collections import Counter
def anagram_windows(s, pattern):
need = Counter(pattern)
window = Counter()
result = []
k = len(pattern)
for right, ch in enumerate(s):
window[ch] += 1
if right >= k:
left_ch = s[right - k]
window[left_ch] -= 1
if window[left_ch] == 0:
del window[left_ch]
if window == need:
result.append(right - k + 1)
return resultWindow frequencies are updated only for the entering and leaving items, avoiding repeated counting. Removing zero counts keeps comparisons and state easier to reason about.
def max_window_sum(nums, k):
current = sum(nums[:k])
best = current
for right in range(k, len(nums)):
current += nums[right] - nums[right - k]
best = max(best, current)
return bestThe next window sum is derived by subtracting the left item and adding the new right item. Initialize the maximum with the first complete window rather than zero when values may be negative.
def range_sum(prefix, left, right):
return prefix[right] - prefix[left]
nums = [2, 4, 1]
prefix = [0]
for x in nums:
prefix.append(prefix[-1] + x)
value = range_sum(prefix, 1, 3)A prefix array with an initial zero returns the sum of the half-open range [left, right) in O(1). The extra zero simplifies boundaries and ranges starting at the first item.
nums = [3, 8, 10, 15]
diffs = [b - a for a, b in zip(nums, nums[1:])]Adjacent differences describe changes between positions and can reconstruct values through accumulation. The result contains one fewer item than the original sequence.
from itertools import accumulate
prefix = list(accumulate([2, 4, 1], initial=0))
products = list(accumulate([2, 3, 4], lambda a, b: a * b))accumulate lazily yields intermediate accumulated results and accepts a custom binary function. initial adds a starting value to the output and changes its length.
def has_subarray_sum(nums, target):
seen = {0}
prefix = 0
for x in nums:
prefix += x
if prefix - target in seen:
return True
seen.add(prefix)
return FalseA subarray sums to target when two prefix sums differ by target. A set of seen prefixes gives O(n) and works with negative values, unlike a standard sum-based sliding window.
def first_true(low, high, feasible):
while low < high:
mid = (low + high) // 2
if feasible(mid):
high = mid
else:
low = mid + 1
return lowBinary search on the answer finds the boundary where a monotonic predicate changes from False to True. The main challenge is choosing consistent inclusive bounds and proving that feasible is monotonic.
Rekurzia a dynamické programovanie10
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)The base case stops recursion, while the recursive step reduces the problem to a smaller instance. Without a reachable base case, calls end with RecursionError.
import sys
sys.setrecursionlimit(200000)Python limits stack depth and raises RecursionError when it is exceeded. Increase the limit carefully because very deep recursion can exhaust the system stack.
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return factorial_tail(n - 1, acc * n)A tail call is the final operation of a function, but Python does not optimize tail calls. Tail recursion still consumes stack space and is usually replaced with a loop.
from functools import cache, lru_cache
@lru_cache(maxsize=None)
def fib_lru(n):
return n if n < 2 else fib_lru(n - 1) + fib_lru(n - 2)
@cache
def fib_cache(n):
return n if n < 2 else fib_cache(n - 1) + fib_cache(n - 2)These decorators store results by arguments and eliminate repeated computations. cache is equivalent to an unlimited lru_cache, and all arguments must be hashable.
def fib(n):
if n < 2:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]Bottom-up DP computes states from simpler to more complex ones and stores them in an array. It avoids the recursion stack, although the array can sometimes be reduced to a few variables.
def grid_paths(rows, cols):
dp = [[0] * cols for _ in range(rows)]
for r in range(rows):
dp[r][0] = 1
for c in range(cols):
dp[0][c] = 1
for r in range(1, rows):
for c in range(1, cols):
dp[r][c] = dp[r - 1][c] + dp[r][c - 1]
return dp[-1][-1]A two-dimensional table is used when a state depends on two parameters, such as a row and column position. Its boundaries must be initialized correctly before filling the remaining cells.
def min_coins(coins, amount):
inf = amount + 1
dp = [0] + [inf] * amount
prev = [-1] * (amount + 1)
for total in range(1, amount + 1):
for coin in coins:
if coin <= total and dp[total - coin] + 1 < dp[total]:
dp[total] = dp[total - coin] + 1
prev[total] = coin
if dp[amount] == inf:
return None
answer = []
while amount > 0:
coin = prev[amount]
answer.append(coin)
amount -= coin
return answerBesides the optimal value, DP can store the choice used to reach each state. The actual answer is then reconstructed by walking backward from the final state.
def merge_sort(a):
if len(a) <= 1:
return a
mid = len(a) // 2
left = merge_sort(a[:mid])
right = merge_sort(a[mid:])
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
return result + left[i:] + right[j:]The problem is split into independent subproblems whose recursive solutions are combined afterward. Efficiency depends on balanced splitting and the cost of combining results.
def permutations(a):
result = []
def backtrack(start):
if start == len(a):
result.append(a.copy())
return
for i in range(start, len(a)):
a[start], a[i] = a[i], a[start]
backtrack(start + 1)
a[start], a[i] = a[i], a[start]
backtrack(0)
return resultBacktracking chooses an option, recurses, and then restores the modified state. Generating every permutation requires factorial time.
def subsets(a):
result = []
current = []
def backtrack(index):
if index == len(a):
result.append(current.copy())
return
backtrack(index + 1)
current.append(a[index])
backtrack(index + 1)
current.pop()
backtrack(0)
return resultFor each element, recursion considers two choices: include it or skip it. The current result must be copied when saved and rolled back after the recursive call.
Grafy a stromy10
edges = [(0, 1), (0, 2), (1, 2)]
graph = {0: [], 1: [], 2: []}
for u, v in edges:
graph[u].append(v)
graph[v].append(u)An adjacency list stores the neighbors of every vertex and usually requires O(V + E) memory. For a directed graph, the reverse edge must not be added automatically.
from collections import deque
def bfs(graph, start):
queue = deque([start])
visited = {start}
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return orderBFS explores a graph level by level with a queue and finds shortest distances in an unweighted graph. Mark a vertex as visited when it enters the queue to avoid duplicate insertions.
def dfs_recursive(graph, start):
visited = set()
order = []
def dfs(node):
visited.add(node)
order.append(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs(neighbor)
dfs(start)
return orderRecursive DFS fully explores one branch before moving to the next. On long chains, it can exceed the Python recursion limit.
def dfs_iterative(graph, start):
stack = [start]
visited = set()
order = []
while stack:
node = stack.pop()
if node in visited:
continue
visited.add(node)
order.append(node)
stack.extend(reversed(graph[node]))
return orderIterative DFS uses an explicit stack and does not depend on the recursion limit. Traversal order depends on the order in which neighbors are pushed.
from collections import deque
def shortest_path(graph, start, target):
queue = deque([start])
parent = {start: None}
while queue:
node = queue.popleft()
if node == target:
break
for neighbor in graph[node]:
if neighbor not in parent:
parent[neighbor] = node
queue.append(neighbor)
if target not in parent:
return None
path = []
node = target
while node is not None:
path.append(node)
node = parent[node]
return path[::-1]BFS first reaches a vertex through a path with the minimum number of edges. Store the parent of each newly visited vertex to reconstruct the path itself.
def preorder(node, result):
if node is None:
return
result.append(node.val)
preorder(node.left, result)
preorder(node.right, result)
def inorder(node, result):
if node is None:
return
inorder(node.left, result)
result.append(node.val)
inorder(node.right, result)
def postorder(node, result):
if node is None:
return
postorder(node.left, result)
postorder(node.right, result)
result.append(node.val)Preorder processes a node before its children, inorder between the left and right subtrees, and postorder after its children. Inorder is sorted only for a valid binary search tree.
from collections import deque
def level_order(root):
if root is None:
return []
queue = deque([root])
levels = []
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
levels.append(level)
return levelsLevel-order traversal is BFS applied to a tree and uses a queue. The queue size at the start of an iteration fixes the number of nodes in the current level.
def has_cycle(graph):
state = {}
def dfs(node):
state[node] = 1
for neighbor in graph[node]:
if state.get(neighbor, 0) == 1:
return True
if state.get(neighbor, 0) == 0 and dfs(neighbor):
return True
state[node] = 2
return False
return any(state.get(node, 0) == 0 and dfs(node) for node in graph)In a directed graph, an edge to a vertex currently in the DFS stack indicates a cycle. Three states are commonly used: unvisited, processing, and fully processed.
from collections import deque
def topological_sort(graph):
indegree = {node: 0 for node in graph}
for node in graph:
for neighbor in graph[node]:
indegree[neighbor] += 1
queue = deque(node for node in graph if indegree[node] == 0)
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbor in graph[node]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
return order if len(order) == len(graph) else NoneKahn algorithm repeatedly removes vertices with zero indegree. If not every vertex is processed, the graph contains a cycle and no topological order exists.
class DSU:
def __init__(self, n):
self.parent = list(range(n))
self.size = [1] * n
def find(self, x):
while x != self.parent[x]:
self.parent[x] = self.parent[self.parent[x]]
x = self.parent[x]
return x
def union(self, a, b):
a = self.find(a)
b = self.find(b)
if a == b:
return False
if self.size[a] < self.size[b]:
a, b = b, a
self.parent[b] = a
self.size[a] += self.size[b]
return TrueUnion-Find quickly checks whether vertices belong to the same component and merges components. Path compression and union by size provide nearly constant amortized operation time.
Výnimky10
try:
value = int(text)
except ValueError:
value = 0
else:
value += 1
finally:
closed = Trueexcept runs after an exception, else only after successful completion of try, and finally always runs. Keep only operations whose exceptions are expected inside the try block.
try:
item = data[key]
except KeyError:
item = NoneCatch a specific exception type to avoid hiding unrelated program errors. A bare except is appropriate only in rare cases where the exception is handled correctly or re-raised.
try:
result = values[index] / divisor
except (IndexError, ZeroDivisionError):
result = NoneMultiple exception types with identical handling can be listed in a tuple in one except clause. Use separate except clauses when their handling differs.
def load_number(text):
try:
return int(text)
except ValueError as exc:
raise ValueError("invalid number") from exc
def parse_number(text):
try:
return load_number(text)
except ValueError:
raiseraise creates an exception, while raise without arguments re-raises the current one. The raise ... from ... form preserves an explicit causal chain between exceptions.
class InvalidStateError(Exception):
pass
def require_ready(ready):
if not ready:
raise InvalidStateError("not ready")Custom exceptions inherit from Exception and distinguish domain errors from built-in failures. An empty class with a clear name is often sufficient.
def built_in_errors(data, key, index, value, divisor):
if not isinstance(value, int):
raise TypeError
if divisor == 0:
raise ZeroDivisionError
if key not in data:
raise KeyError(key)
if not -len(data) <= index < len(data):
raise IndexError(index)
if value < 0:
raise ValueError(value)ValueError indicates an invalid value of an acceptable type, while TypeError indicates an invalid type; KeyError and IndexError represent missing keys or indexes. ZeroDivisionError occurs when dividing or taking a remainder by zero.
def divide(a, b):
assert b != 0
return a / bassert checks internal invariants and raises AssertionError when one fails. Assertions are removed when Python runs with -O, so they must not validate user input.
with open("data.txt", encoding="utf-8") as file:
text = file.read()A context manager guarantees resource cleanup after normal completion and after an exception. For files, this means automatic closing when the with block ends.
from contextlib import suppress
from pathlib import Path
path = Path("data.txt")
with suppress(FileNotFoundError):
path.unlink()contextlib.suppress ignores only explicitly listed exception types and suits short, expected cases. Do not use it to hide failures that require handling or logging.
def eafp(data, key):
try:
return data[key]
except KeyError:
return None
def lbyl(data, key):
if key in data:
return data[key]
return NoneEAFP performs the operation first and handles an expected failure, while LBYL checks a condition beforehand. EAFP is often more idiomatic in Python and avoids a gap between checking and acting.
Triedy a objekty10
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, dx, dy):
self.x += dx
self.y += dyA class describes object structure, while __init__ initializes each new instance. self is passed automatically and refers to the current object.
class Counter:
total = 0
def __init__(self):
self.value = 0
Counter.total += 1A class attribute is shared by all instances, while an attribute on self belongs to one object. Mutating a shared class attribute can unexpectedly affect every instance.
class Account:
def __init__(self, balance):
self.balance = balance
@property
def balance(self):
return self._balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError
self._balance = value@property exposes method logic through normal attribute access and can validate reads or writes. The property usually stores data under another attribute name to avoid infinite recursion.
class Temperature:
def __init__(self, value):
self.value = value
@staticmethod
def is_valid(value):
return value >= -273.15
@classmethod
def zero(cls):
return cls(0)@staticmethod receives neither an instance nor a class, while @classmethod receives the class as cls. Class methods are useful as alternative constructors and preserve subclass behavior.
class Base:
def __init__(self, value):
self.value = value
class Child(Base):
def __init__(self, value, extra):
super().__init__(value)
self.extra = extraA subclass inherits behavior from its base class and can extend or override it. super() calls the next implementation in the method resolution order, which matters especially with multiple inheritance.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point(x={self.x}, y={self.y})'
def __str__(self):
return f'{self.x}, {self.y}'__repr__ is intended for developers and debugging, while __str__ provides user-friendly output. If __str__ is missing, str() falls back to __repr__.
class User:
def __init__(self, user_id):
self.user_id = user_id
def __eq__(self, other):
return isinstance(other, User) and self.user_id == other.user_id
def __hash__(self):
return hash(self.user_id)__eq__ defines logical equality, while __hash__ allows an object to be used in sets and as a dictionary key. Equal objects must have equal hashes, and hashed fields must not change.
class Item:
def __init__(self, score, name):
self.score = score
self.name = name
def __lt__(self, other):
return (self.score, self.name) < (other.score, other.name)
items = sorted([Item(2, 'b'), Item(1, 'a')])__lt__ defines the < operator and lets sorted() compare objects directly. Tuple comparison conveniently expresses primary and tie-breaking sort criteria.
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Node:
value: int
tags: tuple[str, ...] = ()
@dataclass
class Basket:
items: list[str] = field(default_factory=list)@dataclass generates methods such as __init__ and __repr__, while frozen=True blocks normal field assignment. An immutable default can be written directly, but a mutable one such as [] raises ValueError at class creation and must use default_factory.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y__slots__ restricts available instance attributes and often reduces memory usage. Unless __dict__ is included, arbitrary new attributes cannot be added dynamically.
Typové anotácie10
count: int = 3
def repeat(text: str, times: int) -> str:
return text * times
count = '3'Annotations describe expected types for static analyzers, IDEs, and documentation. Python does not enforce them automatically at runtime and still permits values of another type.
items: list[int] = [1, 2]
scores: dict[str, int] = {'a': 1}
point: tuple[int, int] = (2, 3)Built-in collections can be parameterized with element, key, and value types. tuple[int, int] describes a fixed structure, while tuple[int, ...] describes an arbitrary-length tuple.
from typing import Optional
left: Optional[int] = None
right: int | None = NoneOptional[int] and int | None both mean that a value may be an int or None. Optional does not make a parameter optional; that requires a default argument value.
from typing import Union
value_a: Union[int, str] = 1
value_b: int | str = 'x'Union[int, str] and int | str allow one of several types. Very broad unions make type narrowing and exhaustive handling more difficult.
from typing import Any
value: Any = 1
value = 'x'
value = [1, 2]Any disables static checks for operations on a value and is compatible with every type in both directions. Overusing it hides errors and removes much of the benefit of annotations.
from collections.abc import Callable
def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
return fn(a, b)Callable[[A, B], R] describes a callable taking two arguments and returning R. Callable[..., R] is used when the precise parameter list is unknown.
from collections.abc import Iterable, Sequence
def total(values: Iterable[int]) -> int:
return sum(values)
def first(values: Sequence[int]) -> int:
return values[0]Iterable guarantees iteration but not indexing or repeated traversal. Sequence additionally provides length and indexed access, making it a stricter contract.
from typing import Literal, TypeAlias
UserId: TypeAlias = int
Mode: TypeAlias = Literal['fast', 'safe']TypeAlias gives a readable name to another type but does not create a distinct incompatible type. Literal restricts a value to specific literals and is useful for modes and states.
from typing import TypedDict
class UserData(TypedDict):
name: str
age: int
user: UserData = {'name': 'a', 'age': 20}TypedDict describes required keys and value types for an ordinary dictionary. At runtime the object remains a dict, and its structure is checked only by static tools.
from typing import Generic, TypeVar
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, value: T):
self.value = value
def get(self) -> T:
return self.valueTypeVar links types across a signature, while Generic creates a class parameterized by that type. Unlike Any, a type checker preserves the concrete type for each use.
Testy a ladenie10
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5pytest discovers functions whose name starts with test (default python_functions = test*; test_ by convention) and treats a failed assert as a test failure. Assertions should compare concrete results so failure reports can show useful differences.
import pytest
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0pytest.raises verifies that a block raises the expected exception type. The test fails if no exception is raised or if its type differs.
import pytest
@pytest.mark.parametrize('x, expected', [(1, 1), (2, 4), (3, 9)])
def test_square(x, expected):
assert x * x == expectedparametrize runs one test function for every supplied data set. Cases remain independent, so the report identifies the exact failing input.
import pytest
@pytest.fixture
def sample():
return [1, 2, 3]
def test_length(sample):
assert len(sample) == 3A fixture prepares data or resources and is injected into a test through a matching parameter name. Overly broad fixtures create hidden dependencies and make tests harder to understand.
import pytest
def test_ratio():
assert 0.1 + 0.2 == pytest.approx(0.3)pytest.approx compares numbers using an allowed absolute or relative tolerance. Direct float equality is unreliable because many decimal fractions have no exact binary representation.
import unittest
class MathTest(unittest.TestCase):
def test_add(self):
self.assertEqual(2 + 3, 5)unittest.TestCase is the standard-library class-based testing model with methods such as assertEqual, setUp, and tearDown. A test method name must start with the prefix test (TestLoader.testMethodPrefix) for the default loader to discover it; test_ is the usual convention.
def square(x):
"""
>>> square(3)
9
"""
return x * x
if __name__ == '__main__':
import doctest
doctest.testmod()doctest executes examples from docstrings and compares their output with the recorded result. It is useful for small examples but can be fragile when output formatting changes.
import logging
value = 3
print(value)
logging.basicConfig(level=logging.DEBUG)
logging.debug('%s', value)print is convenient for quick local inspection, while logging supports levels, formatting, and configurable destinations. Logging is generally preferable in libraries and server applications.
from timeit import timeit
elapsed = timeit('sum(range(100))', number=10_000)timeit repeatedly executes a small statement and reduces the impact of random timing noise. Comparisons must use equivalent work under the same conditions rather than relying on one run.
import traceback
try:
1 / 0
except ZeroDivisionError:
traceback.print_exc()traceback.print_exc() prints the active traceback from inside an exception handler. Catching an error without logging or re-raising it is dangerous because the original failure becomes hidden.
Vstup, výstup a súbory10
name = input().strip()`input` reads one line from standard input, removes the trailing newline and always returns a `str`. Leading and trailing spaces remain unless `strip` is called separately.
import sys
tokens = sys.stdin.buffer.read().split()`sys.stdin.buffer.read` quickly reads all input as `bytes`, and `split` separates it on arbitrary whitespace. This approach is convenient for algorithmic problems but keeps the entire input in memory.
import sys
while line := sys.stdin.readline():
line = line.rstrip("\r\n")`sys.stdin.readline` preserves the trailing newline and returns an empty string at end of input. Reading line by line avoids loading the entire input into memory.
import sys
print(a, b, sep=",", end="\n", file=sys.stderr)`sep` defines the separator between arguments, `end` defines the output suffix, and `file` selects the target text stream. Many individual `print` calls can be slower than building one string with `join`.
with open("input.txt", "r", encoding="utf-8") as f:
text = f.read()
with open("output.txt", "w", encoding="utf-8") as f:
f.write(text)`with open` guarantees that a file is closed even if an exception occurs, and an explicit encoding makes behaviour portable. Mode `w` truncates an existing file before writing.
import json
data = json.loads(text)
text = json.dumps(data, ensure_ascii=False)`json.loads` parses a JSON string into Python objects, while `json.dumps` performs the reverse conversion. JSON does not preserve the distinction between lists and tuples, and object keys are strings after parsing.
import csv
with open("data.csv", newline="", encoding="utf-8") as f:
rows = list(csv.reader(f))
with open("out.csv", "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(rows)`csv.reader` and `csv.writer` correctly handle delimiters, quoting and line breaks inside fields. CSV files should be opened with `newline=""`, and read values remain strings by default.
from pathlib import Path
path = Path("data") / "input.txt"
text = path.read_text(encoding="utf-8")
path.write_text(text, encoding="utf-8")`pathlib.Path` creates portable path objects and joins path components with `/`. `read_text` and `write_text` are convenient for small files but process the entire content at once.
import os
token = os.environ.get("API_TOKEN")`os.environ` exposes process environment variables as a mutable mapping of strings. Square-bracket access raises `KeyError` for a missing key, while `get` returns `None` or a supplied default.
import sys
script, *args = sys.argv`sys.argv` contains command-line arguments as strings, with index `0` usually holding the executed script name. It performs no validation or conversion; use `argparse` for a structured command-line interface.