当前位置:首页>python>写 Python 五年回头看,这 3 个技能最值得投资

写 Python 五年回头看,这 3 个技能最值得投资

  • 2026-09-02 16:37:02
写 Python 五年回头看,这 3 个技能最值得投资
一个批处理脚本跑到最后,只扔出一行:
KeyError: 'user_id'

没有文件名,没有数据行号,也不知道是哪条脏数据。开发机上重新跑一遍,又正常。

这种代码我现在看到,第一反应不是修 KeyError,而是先嫌弃:程序写得再快,出了问题只能靠猜,等于没写完。

写 Python 五年后再回头看,真正拉开差距的,不是背了多少语法,也不是会不会用某个热门框架。下面这三个技能,越早花时间,后面返工越少。

让代码自己留下证据

刚写 Python 时,我也爱到处 print()。

print("开始处理")
print(data)
print("处理失败")

本地看着热闹,一上定时任务就不行了。几百个文件同时处理,日志混在一起,根本不知道哪条信息属于哪个任务。

后来我写批处理,入口处一定先把上下文绑上。文件名、批次号、数据位置,能带的都带上。

import logging
from pathlib import Path

logger = logging.getLogger("bill_import")


defload_bill(file_path: Path, batch_no: str) -> list[dict]:
    rows: list[dict] = []

with file_path.open("r", encoding="utf-8") as stream:
for line_no, raw_line in enumerate(stream, start=1):
try:
                bill_no, amount = raw_line.rstrip().split("|")
                rows.append({
"bill_no": bill_no,
"amount": int(amount),
                })
except (ValueError, TypeError):
                logger.exception(
"账单行解析失败 batch=%s file=%s line=%d raw=%r",
                    batch_no,
                    file_path.name,
                    line_no,
                    raw_line[:120],
                )

return rows

这段代码没什么花活,但真出问题时,可以直接定位到批次、文件和行号。

异常也别随手吞。

try:
    sync_order(order)
except Exception:
returnFalse

这种写法最省代码,也最费排查时间。至少把异常类型、业务主键和当前动作留下来。线上故障不怕报错,怕的是报错之后没有痕迹。

日志、指标、异常上下文,这些东西平时感觉多余,出一次问题就知道值钱了。

学会控制数据,而不是一把塞进内存

Python 写数据处理太顺手了,顺手到很多人上来就是:

records = file.read().splitlines()
result = [clean(item) for item in records]

文件小时没问题。文件一大,内存先替代码扛不住。

五年里我越来越依赖生成器,不是因为它“高级”,而是它能逼着代码按流处理。数据来一条,处理一条,不需要先把整个世界装进列表。

from collections.abc import Iterator
from pathlib import Path


defiter_device_codes(file_path: Path) -> Iterator[str]:
with file_path.open("r", encoding="utf-8") as stream:
for raw_line in stream:
            code = raw_line.strip().upper()

ifnot code or code.startswith("#"):
continue

if len(code) < 8:
continue

yield code


defcollect_batches(
    codes: Iterator[str],
    batch_size: int = 300,
)
 -> Iterator[list[str]]:

    bucket: list[str] = []

for code in codes:
        bucket.append(code)

if len(bucket) == batch_size:
yield bucket
            bucket = []

if bucket:
yield bucket

调用时也很直接:

for batch in collect_batches(iter_device_codes(Path("devices.txt"))):
    push_to_api(batch)

这里值得投资的不是记住 yield,而是建立一种处理习惯:不要默认数据量永远很小,不要默认接口一次能吃完,也不要默认中间结果必须全部留在内存里。

生成器、上下文管理器、pathlib、collections 这些标准库,我现在的使用频率远高于很多第三方包。

框架会换,标准库里的基本功没那么容易过期。

把边界写清楚

Python 最容易把人带进一个坑:反正动态类型,先跑起来再说。

于是一个函数里的 payload,有时是字典,有时是 None,偶尔还是字符串。调用方只能靠运气。

这种代码短期写得快,后面改一次要翻半个项目。我现在更愿意在入口处多写几行,把数据边界卡死。

from dataclasses import dataclass
from decimal import Decimal, InvalidOperation


@dataclass(frozen=True)
classRefundRequest:
    order_no: str
    amount: Decimal


defparse_refund(payload: dict[str, object]) -> RefundRequest:
    order_no = str(payload.get("order_no", "")).strip()
    raw_amount = payload.get("amount")

ifnot order_no:
raise ValueError("order_no 不能为空")

try:
        amount = Decimal(str(raw_amount))
except (InvalidOperation, ValueError):
raise ValueError(f"退款金额非法: {raw_amount!r}") fromNone

if amount <= 0:
raise ValueError("退款金额必须大于 0")

return RefundRequest(order_no=order_no, amount=amount)

再给关键边界补两个测试:

import pytest


deftest_parse_refund_rejects_empty_order_no() -> None:
with pytest.raises(ValueError, match="order_no"):
        parse_refund({"order_no": " ", "amount": "12.50"})


deftest_parse_refund_keeps_decimal_precision() -> None:
    request = parse_refund({
"order_no": "R2026008",
"amount": "19.90",
    })

assert request.amount == Decimal("19.90")

类型标注、数据模型、边界校验、自动化测试,放在一起才有意义。只写类型标注,不检查输入,还是会在运行时炸;只写测试,不收紧函数职责,测试也会越来越难维护。

我现在判断一段 Python 代码靠不靠谱,通常不会先看它用了什么技巧,而是看三个地方:出错后能不能查,数据变大后会不会垮,输入失控时能不能及时拦住。

语法熟练当然重要,但半年基本就够用了。后面几年真正值得反复投资的,是可观察性、数据处理方式和工程边界。

这些东西不太适合拿来炫技。

但凌晨处理故障时,能少翻几十分钟日志。

最新文章

随机文章