Паттерны и идиоматичный Python
EAFP vs LBYL
LBYL (Look Before You Leap) — проверяй перед действием. EAFP (Easier to Ask Forgiveness than Permission) — делай и обработай ошибку.
# LBYL — check first
if key in data:
return data[key]
return None
# EAFP — try first (Pythonic!)
try:
return data[key]
except KeyError:
return None
EAFP предпочтителен в Python: нет race condition, исключения дешёвые, код чище.
# LBYL: race condition between check and open
if os.path.exists("file.txt"):
with open("file.txt") as f: # File might be deleted!
data = f.read()
# EAFP: just try to open
try:
with open("file.txt") as f:
data = f.read()
except FileNotFoundError:
data = "default"
Pythonic-идиомы
Распаковка и swap
a, b = 1, 2
a, b = b, a # No temp variable
first, *rest = [1, 2, 3, 4, 5]
# first=1, rest=[2,3,4,5]
first, *middle, last = [1, 2, 3, 4, 5]
# middle=[2,3,4]
# Unpack in loops
users = [("Алексей", 30), ("Мария", 25)]
for name, age in users:
print(f"{name}: {age}")
Comprehensions
squares = [x ** 2 for x in range(10)]
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
word_lengths = {word: len(word) for word in ["Python", "Java", "Go"]}
unique_lengths = {len(word) for word in ["hello", "world", "hi"]}
# Flatten nested list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
enumerate и zip
fruits = ["яблоко", "банан", "вишня"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
names = ["Алексей", "Мария"]
ages = [30, 25]
for name, age in zip(names, ages):
print(f"{name}: {age}")
user_dict = dict(zip(names, ages))
Тернарный оператор и walrus operator
status = "active" if user.is_active else "inactive"
# Walrus operator := (Python 3.8+)
if (n := len(items)) > 10:
print(f"Слишком много: {n}")
# Useful in while loops
while (line := input("> ")) != "quit":
print(f"Вы ввели: {line}")
any() и all()
numbers = [2, 4, 6, 8, 10]
all_even = all(n % 2 == 0 for n in numbers) # True
has_large = any(n > 5 for n in numbers) # True
# Validate all required fields
user = {"name": "Алексей", "email": "[email protected]", "age": 30}
required = ["name", "email", "age"]
is_valid = all(key in user and user[key] for key in required)
Контекстные менеджеры
from contextlib import contextmanager, suppress
import time
@contextmanager
def timer(label: str):
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f}с")
with timer("Вычисление"):
sum(range(1_000_000))
# Suppress specific exceptions
with suppress(FileNotFoundError):
os.remove("temp.txt")
Антипаттерны
# 1: Mutable default argument
# BAD:
def add_item(item, items=[]): # Shared between calls!
items.append(item)
return items
# GOOD:
def add_item(item, items: list | None = None) -> list:
if items is None:
items = []
items.append(item)
return items
# 2: Bare except
# BAD:
try:
result = do_something()
except: # Catches KeyboardInterrupt too!
pass
# GOOD:
try:
result = do_something()
except (ValueError, TypeError) as e:
logger.error("Error: %s", e)
# 3: String concatenation in loop
# BAD (O(n^2)):
result = ""
for word in words:
result += word + " "
# GOOD (O(n)):
result = " ".join(words)
# 4: Not using dict.get()
# BAD:
if key in data:
value = data[key]
else:
value = default
# GOOD:
value = data.get(key, default)
# 5: Boolean comparison
# BAD:
if is_active == True: ...
if items != []: ...
# GOOD:
if is_active: ...
if items: ...
Pattern Matching (Python 3.10+)
def handle_command(command: dict) -> str:
match command:
case {"action": "create", "name": str(name)}:
return f"Создание: {name}"
case {"action": "delete", "id": int(item_id)}:
return f"Удаление #{item_id}"
case {"action": "list", "filter": str(f)}:
return f"Список с фильтром: {f}"
case {"action": "list"}:
return "Полный список"
case _:
return "Неизвестная команда"
print(handle_command({"action": "create", "name": "Проект"}))
print(handle_command({"action": "delete", "id": 42}))
Zen of Python
import this
# Key principles:
# Explicit > Implicit — make dependencies explicit
# Simple > Complex — use standard data structures
# Flat > Nested — avoid deep nesting (early return)
# Readability counts — write for humans
# Errors should never pass silently — don't suppress exceptions