← 返回信息流
AI 资讯Hacker News·3 小时前

减少假设,释放代码潜力

原标题:Reducing Assumptions, Exploding Your Code

速览

编程中的许多性能问题和bug源于隐含的假设。本文提出系统性地识别并移除这些假设,从而让代码运行更快、更稳定。这种方法适用于各种语言和框架,能显著提升软件质量。它强调开发者应主动质疑默认行为,以释放代码的真正潜力。

AI 深度解读

背景

在软件开发中,编写优雅、简洁的脚本往往是我们的追求。但现实世界充满不确定性:用户输入可能错误、文件可能缺失、网络请求可能失败。一个只假设“一切顺利”的脚本虽然在理想路径下看起来完美,却无法处理现实中的各种异常。当我们为脚本添加验证和错误处理时,代码往往会变得臃肿、结构混乱,原本清晰的逻辑被淹没在各种守卫条件中。这篇来自 Hacker News 的文章以一个小型 PDF 下载脚本为例,展示了从“优雅假设”到“完整错误处理”的演变过程,并对比了 Python 和 Rye 两种语言在处理这一问题时的不同表现。

核心内容

文章从一个简单的脚本开始:脚本接受一个 ID 作为参数,从 setup.json 中读取 API 令牌,向远程服务器发起 HTTP 请求下载一个 PDF 文件,文件名由服务器返回的 Content-Disposition 头部决定。

初始版本:优雅但充满假设

Python 版本(约 15 行):

import sys, json, requests, re
from requests.auth import HTTPBasicAuth

id = int(sys.argv[1])
with open('setup.json') as f:
    setup = json.load(f)

url = f"https://www.example.com/pdf-api?id={id}"
resp = requests.get(url, auth=HTTPBasicAuth(setup['token'], 'x'))

pattern = re.compile(r"filename\*?=[f']?(.*?)[']?(?:;?$)")
content_disp = resp.headers['Content-Disposition']
filename = pattern.search(content_disp).group(1)

with open(filename, 'wb') as f:
    f.write(resp.content)

Rye 版本(Rye 语言版本,约 8 行):

rye .Args? .load .first :id
Load %setup.rye |context :setup
re: regexp "filename\*?=[f']?(.*?)[']?(?:;?$)"
format id https://www.example.com/pdf-api?id=%d
|Request 'GET ""
|Basic-auth! setup/token "x"
|Call :resp
|Header? "Content-Disposition" |Submatch?* re
|file .Create
|Copy* Reader resp

这两个版本都假设:

  • 脚本总是收到一个整数参数;
  • setup.json 文件存在且内容正确;
  • HTTP 请求永远不会失败;
  • Content-Disposition 头部总是存在且包含文件名;
  • 总是能够创建新文件并写入。

添加基本验证(第2步)

为了防止上述假设被打破,我们添加了基本输入验证。

Python 版本(约 25 行):

import sys, json, requests, re
from requests.auth import HTTPBasicAuth

if len(sys.argv) != 2:
    raise ValueError("script argument id - expected exactly one integer")
try:
    id = int(sys.argv[1])
except ValueError:
    raise ValueError("script argument id - must be an integer")

with open('setup.json') as f:
    setup = json.load(f)

if 'token' not in setup or not isinstance(setup['token'], str):
    raise ValueError("loading setup - token field required as string")

url = f"https://www.example.com/pdf-api?id={id}"
resp = requests.get(url, auth=HTTPBasicAuth(setup['token'], 'x'))

pattern = re.compile(r"filename\*?=[f']?(.*?)[']?(?:;?$)")
content_disp = resp.headers['Content-Disposition']
filename = pattern.search(content_disp).group(1)

with open(filename, 'wb') as f:
    f.write(resp.content)

添加了几行检查后,原本优雅的脚本已经变得支离破碎。try/catch 结构破坏了代码的流畅性。

Rye 版本:利用验证方言(validation dialect)和 .defer\ 'Close 确保资源清理,代码结构几乎没有变化:

rye .Args? .validate { <one> integer }
|check "script argument id" |first :id
Load %setup.rye |context |validate { token: required string }
|check "setup file" :setup
re: regexp "filename\*?=[f']?(.*?)[']?(?:;?$)"
format id https://www.example.com/pdf-api?id=%d
|Request 'GET ""
|Basic-auth! setup/token "x"
|Call :resp
|Header? "Content-Disposition" |Submatch?* re
|file .Create .defer\ 'Close
|Copy* resp .Reader .defer\ 'Close

完整错误处理(第3步)

现在处理所有可能的失败,并提供友好的用户反馈。

Python 版本(约 45 行):

import sys, json, requests, re
from requests.auth import HTTPBasicAuth

# Validate arguments
if len(sys.argv) != 2:
    print("Error: script argument id - expected exactly one integer")
    sys.exit(1)

try:
    id = int(sys.argv[1])
except ValueError:
    print("Error: script argument id - must be an integer")
    sys.exit(1)

# Load and validate config
try:
    with open('setup.json') as f:
        setup = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
    print(f"Error: couldn't open config - {e}")
    sys.exit(1)

if 'token' not in setup or not isinstance(setup['token'], str):
    print("Error: loading setup - token field required as string")
    sys.exit(1)

pattern = re.compile(r"filename\*?=[f']?(.*?)[']?(?:;?$)")
url = f"https://www.example.com/pdf-api?id={id}"

try:
    resp = requests.get(url, auth=HTTPBasicAuth(setup['token'], 'x'))
    resp.raise_for_status()
except requests.RequestException as e:
    print(f"Error: Http request failed - {e}")
    sys.exit(1)

# Extract filename with default fallback
content_disp = resp.headers.get('Content-Disposition', '')
match = pattern.search(content_disp) if content_disp else None
filename = match.group(1) if match else "default.pdf"

try:
    with open(filename, 'wb') as f:
        f.write(resp.content)
except IOError as e:
    print(f"Error: couldn't create local pdf - {e}")
    sys.exit(1)
except Exception as e:
    print(f"Error: couldn't save contents - {e}")
    sys.exit(1)

原本 15 行的脚本膨胀到 45 行,大量结构代码将实际工作逻辑隐藏起来。Python 程序员一般会用辅助函数、argparse 和验证库来重构,但这仍然增加了结构(或依赖)。

Rye 版本:通过 |^check|fix 等内置函数,保持行数基本不变,代码结构完全未变:

rye .Args? .validate { <one> integer }
|^check "script argument id" |first :id
Load %setup.rye |check "couldn't open setup file" |context
|validate { token: required string } |^check "loading setup" :setup
re: regexp "filename\*?=[f']?(.*?)[']?(?:;?$)"
format id https://www.example.com/pdf-api?id=%d
|Request 'GET ""
|Basic-auth! setup/token "x"
|Call |^check "Http request failed" :resp
|Header? "Content-Disposition"
|Submatch?* re |fix { "default.pdf" }
|file .Create |^check "couldn't create local pdf" |defer\ 'Close
|Copy* resp .Reader .defer\ 'Close |^check "couldn't save contents"

Rye 版本不仅行数几乎没变,程序的结构 —— 一条数据流管道 —— 始终如一。

核心设计原理

Rye 中的 check 函数:如果值不是失败值,则返回原值;否则将失败封装为更高级别的失败并返回。

查看原文 →ryelang.org