返回 Skill 列表
extension
分类: 开发与工程无需 API Key

pythonic-conventions

Python的核心惯用法和约定。在编写或审查Python代码时应用这些规则,以确保使用诸如推导式、内置函数、上下文管理器和解包等惯用模式。

person作者: jakexiaohubgithub

Pythonic Conventions

Essential Python idioms that make code more readable, concise, and efficient.

Quick Reference

| Pattern | Use Instead Of | |---------|----------------| | List/dict/set comprehensions | Manual loops to build collections | | enumerate() | Manual index tracking | | zip() | Manual parallel iteration | | any() / all() | Loop with flag variable | | Context managers (with) | Manual resource cleanup | | Unpacking | Index access for known structures | | in operator | Manual membership loops | | Walrus operator (:=) | Separate assignment + condition | | Generator expressions | List comprehension when iterating once | | defaultdict / Counter | Manual dict initialization |


Comprehensions Over Loops

Use comprehensions to build lists, dicts, and sets. They're more readable and often faster.

List Comprehensions

# INCORRECT - manual loop
squares = []
for x in range(10):
    squares.append(x ** 2)

# CORRECT - list comprehension
squares = [x ** 2 for x in range(10)]

# CORRECT - with condition
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]

Dict Comprehensions

# INCORRECT - manual loop
name_to_length = {}
for name in names:
    name_to_length[name] = len(name)

# CORRECT - dict comprehension
name_to_length = {name: len(name) for name in names}

# CORRECT - transform keys/values
upper_mapping = {k.upper(): v * 2 for k, v in mapping.items()}

Set Comprehensions

# INCORRECT - manual loop
unique_lengths = set()
for word in words:
    unique_lengths.add(len(word))

# CORRECT - set comprehension
unique_lengths = {len(word) for word in words}

When NOT to Use Comprehensions

# INCORRECT - comprehension too complex (nested + multiple conditions)
result = [
    transform(x, y)
    for x in items
    if is_valid(x)
    for y in x.children
    if y.active and y.score > threshold
]

# CORRECT - use a loop for complex logic
result = []
for x in items:
    if not is_valid(x):
        continue
    for y in x.children:
        if y.active and y.score > threshold:
            result.append(transform(x, y))

Built-in Functions

enumerate() for Index + Value

# INCORRECT - manual index tracking
i = 0
for item in items:
    print(f"{i}: {item}")
    i += 1

# INCORRECT - range with index access
for i in range(len(items)):
    print(f"{i}: {items[i]}")

# CORRECT - enumerate
for i, item in enumerate(items):
    print(f"{i}: {item}")

# CORRECT - custom start index
for i, item in enumerate(items, start=1):
    print(f"{i}: {item}")

zip() for Parallel Iteration

# INCORRECT - manual parallel iteration
for i in range(len(names)):
    print(f"{names[i]}: {scores[i]}")

# CORRECT - zip
for name, score in zip(names, scores):
    print(f"{name}: {score}")

# CORRECT - strict mode (raises on length mismatch)
for name, score in zip(names, scores, strict=True):
    print(f"{name}: {score}")

any() and all() for Boolean Checks

# INCORRECT - loop with flag
has_negative = False
for x in numbers:
    if x < 0:
        has_negative = True
        break

# CORRECT - any()
has_negative = any(x < 0 for x in numbers)

# INCORRECT - loop with flag for all
all_positive = True
for x in numbers:
    if x <= 0:
        all_positive = False
        break

# CORRECT - all()
all_positive = all(x > 0 for x in numbers)

sum(), max(), min() with Generators

# INCORRECT - manual summation
total = 0
for item in items:
    total += item.value

# CORRECT - sum with generator
total = sum(item.value for item in items)

# CORRECT - max/min with key function
oldest = max(users, key=lambda u: u.age)
shortest = min(strings, key=len)

sorted() with Key Functions

# INCORRECT - manual sort key extraction
pairs = [(len(s), s) for s in strings]
pairs.sort()
sorted_strings = [s for _, s in pairs]

# CORRECT - sorted with key
sorted_strings = sorted(strings, key=len)

# CORRECT - sort by multiple criteria
sorted_users = sorted(users, key=lambda u: (u.department, -u.salary))

Context Managers

Always use with for resource management.

# INCORRECT - manual cleanup
f = open("file.txt")
try:
    data = f.read()
finally:
    f.close()

# CORRECT - context manager
with open("file.txt") as f:
    data = f.read()

# CORRECT - multiple resources
with open("input.txt") as infile, open("output.txt", "w") as outfile:
    outfile.write(infile.read())

Common Context Managers

# File I/O
with open(path) as f:
    ...

# Locks
with lock:
    ...

# Database connections
with connection.cursor() as cursor:
    ...

# Temporary directories
with tempfile.TemporaryDirectory() as tmpdir:
    ...

# Suppressing exceptions
with contextlib.suppress(FileNotFoundError):
    os.remove(path)

Unpacking

Tuple/List Unpacking

# INCORRECT - index access
point = (3, 4)
x = point[0]
y = point[1]

# CORRECT - unpacking
x, y = point

# CORRECT - swap without temp variable
a, b = b, a

# CORRECT - extended unpacking
first, *rest = items
first, *middle, last = items

Dict Unpacking

# CORRECT - merge dicts
merged = {**defaults, **overrides}

# CORRECT - function kwargs
config = {"timeout": 30, "retries": 3}
response = fetch_data(url, **config)

Unpacking in Loops

# INCORRECT - index access
for pair in pairs:
    print(f"{pair[0]}: {pair[1]}")

# CORRECT - unpacking
for key, value in pairs:
    print(f"{key}: {value}")

# CORRECT - with enumerate
for i, (key, value) in enumerate(mapping.items()):
    print(f"{i}: {key}={value}")

Membership Testing

# INCORRECT - loop for membership
found = False
for item in items:
    if item == target:
        found = True
        break

# CORRECT - in operator
found = target in items

# CORRECT - use sets for repeated membership tests
allowed = {"admin", "moderator", "user"}
if role in allowed:
    ...

Walrus Operator (:=)

Use assignment expressions to avoid redundant calls or lines.

# INCORRECT - call twice or use temp variable
match = pattern.search(text)
if match:
    process(match.group())

# CORRECT - walrus operator
if match := pattern.search(text):
    process(match.group())

# CORRECT - in while loops
while chunk := file.read(8192):
    process(chunk)

# CORRECT - in comprehensions with filtering
results = [y for x in data if (y := expensive_compute(x)) is not None]

Generators Over Lists

Use generator expressions when you only iterate once.

# INCORRECT - creates full list in memory
total = sum([x ** 2 for x in range(1_000_000)])

# CORRECT - generator expression (no brackets)
total = sum(x ** 2 for x in range(1_000_000))

# CORRECT - generator function for complex logic
def read_large_file(path: Path) -> Iterator[str]:
    with open(path) as f:
        for line in f:
            if line.strip():
                yield line.strip()

Collections Module

defaultdict

# INCORRECT - manual key initialization
groups = {}
for item in items:
    if item.category not in groups:
        groups[item.category] = []
    groups[item.category].append(item)

# CORRECT - defaultdict
from collections import defaultdict

groups = defaultdict(list)
for item in items:
    groups[item.category].append(item)

Counter

# INCORRECT - manual counting
counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1

# CORRECT - Counter
from collections import Counter

counts = Counter(words)
most_common = counts.most_common(10)

String Operations

str.join() for Concatenation

# INCORRECT - concatenation in loop
result = ""
for word in words:
    result += word + " "

# CORRECT - join
result = " ".join(words)

F-strings Over Format/Concatenation

# INCORRECT - old-style formatting
msg = "Hello, %s! You have %d messages." % (name, count)
msg = "Hello, {}! You have {} messages.".format(name, count)

# CORRECT - f-string
msg = f"Hello, {name}! You have {count} messages."

# CORRECT - f-string with expressions
msg = f"Total: {price * quantity:.2f}"

Boolean Expressions

Truthy/Falsy Checks

# INCORRECT - explicit comparison
if len(items) > 0:
    ...
if len(items) == 0:
    ...
if value == None:
    ...
if value == True:
    ...

# CORRECT - truthy/falsy
if items:
    ...
if not items:
    ...
if value is None:
    ...
if value is True:  # Only when specifically checking True, not truthy
    ...
if value:  # Usually preferred for boolean check
    ...

Chained Comparisons

# INCORRECT - separate comparisons
if x > 0 and x < 10:
    ...

# CORRECT - chained comparison
if 0 < x < 10:
    ...

Conditional Expressions

# INCORRECT - if/else for simple assignment
if condition:
    value = x
else:
    value = y

# CORRECT - conditional expression
value = x if condition else y

# CORRECT - with function calls
status = "active" if user.is_active else "inactive"

Dictionary Operations

dict.get() with Default

# INCORRECT - explicit key check
if key in d:
    value = d[key]
else:
    value = default

# CORRECT - get with default
value = d.get(key, default)

defaultdict for Lazy Initialization

# INCORRECT - check then set
if key not in d:
    d[key] = []
d[key].append(item)

# INCORRECT - setdefault (prefer defaultdict for clarity)
d.setdefault(key, []).append(item)

# CORRECT - defaultdict
from collections import defaultdict

d = defaultdict(list)
d[key].append(item)

Exception Handling

EAFP Over LBYL

Easier to Ask Forgiveness than Permission.

# INCORRECT - LBYL (Look Before You Leap)
if key in mapping:
    value = mapping[key]
else:
    value = default

# CORRECT - EAFP
try:
    value = mapping[key]
except KeyError:
    value = default

# Even better for this case
value = mapping.get(key, default)

Specific Exception Types

# INCORRECT - bare except
try:
    result = risky_operation()
except:
    handle_error()

# INCORRECT - too broad
try:
    result = risky_operation()
except Exception:
    handle_error()

# CORRECT - specific exceptions
try:
    result = risky_operation()
except (ValueError, TypeError) as e:
    handle_error(e)