年度试错额度:用 Python 给自己发一张“合法浪费”许可证。
说明:本文为纯技术实践分享,不涉及任何课程推广、营销引流或商业产品。所有代码可在本地离线运行。
一、实际应用场景描述
在《心理健康与创新能力》课程中,有一个让我当场愣住的实验:研究者给一组参与者发放了"免费实验券"——告诉他们"这几张券可以用来做任何事,不需要解释理由,也不需要产出结果"。另一组则没有。 结果前者的创新产出质量和数量都显著高于后者,而且他们在后续任务中表现出的探索意愿也更持久。
教授的解释是:"当人们知道自己有'配额'可以浪费时,反而会更勇敢地尝试。"
这让我想起自己每年的状态:年初雄心勃勃列了一堆"要学的新技术""要做的 side project",但到年底复盘时发现,真正动手的不到三分之一。不是因为没时间,而是每次想开始一个新尝试时,脑子里都会自动弹出一个成本核算器:
- 这个要花多少钱买域名和服务器?
- 学这个框架如果最后没用上,时间岂不是白费了?
- 万一做了一半发现方向错了怎么办?
这些计算本身没错——成年人的资源确实有限。但问题是,当你把每一次尝试都当成"投资决策"时,你就失去了"玩耍"的能力。 而课程明确指出:大量突破性创新恰恰来源于"无目的的玩耍"。
于是我写了一个本地命令行工具:
设定一个"年度试错额度"——可以是金额、时间或项目数量——在额度内的任何尝试都被标记为"合法浪费",不需要产出任何收益,也不需要向任何人解释。
典型使用场景:
- 年初设定年度试错额度(如:500元 + 40小时 + 5个项目)
- 想尝试一个新东西时,先检查额度是否充足
- 如果充足,一键"领取试错许可",开始折腾
- 结束后记录实际消耗和体验(不管成功与否)
- 额度用完就停——用纪律保护自由
所有数据保存在本地,不上传、不联网。
二、引入痛点
在个人创新实践中,关于"尝试"这件事,有三个根深蒂固的障碍:
1. 全有全无的思维陷阱
大多数人把"尝试"和"投入"绑定在一起。一旦开始,就觉得必须全力以赴、必须做出成果、必须对得起花掉的时间和钱。这种心态让每一次尝试都变成了一场不能输的赌博——你当然会害怕开始。
2. 隐性成本的无限膨胀
当你没有明确边界时,"试试看"的成本会不断膨胀。本来只想花 50 块买个域名,结果又买了服务器、又买了课程、又花了三天配置环境——最后发现已经投入了 500 块和 20 个小时,却连第一行核心代码都没写。这不是计划的失败,是缺乏预算约束。
3. 缺乏"合法浪费"的心理许可
社会文化和职场环境都在强化一个观念:浪费是可耻的。 时间浪费、金钱浪费、精力浪费——任何没有"产出"的投入都会被贴上负面标签。这让人在尝试新事物时背负了巨大的心理负担,甚至产生愧疚感。
我们需要一个工具来:
- 给"浪费"一个合法的身份——它是配额内的、被允许的
- 用硬边界防止成本无限膨胀
- 降低尝试的心理门槛——"反正还在额度内"
三、核心逻辑讲解
本程序的核心是"年度试错额度管理器",逻辑流程如下:
[年初设定年度额度:金额 + 时间 + 项目数]
↓
[每次尝试前:检查剩余额度]
↓
[额度充足 → 发放"试错许可证"]
↓
[自由尝试,无压力、无 KPI]
↓
[结束后如实记录实际消耗]
↓
[额度自动扣减,余额更新]
↓
[年末复盘:额度使用效率和体验质量]
关键设计点
1. 三维额度模型
额度不只是钱,它包含三个维度:
-
"budget":可花费的金额(人民币)
-
"hours":可投入的时间(小时)
-
"projects":可启动的项目数量
这三个维度独立计算。一个项目可能只花 10 块钱但占用了 15 个小时——系统会同时扣减两个维度的额度。
2. "试错许可证"的心理设计
当你申请开始一个新尝试时,程序会生成一张"许可证",上面写着:
- 项目名称
- 批准时间
- 剩余额度
- 一句话提醒:"在额度内,你可以尽情失败。"
这张许可证的意义不是管控,而是心理授权——它告诉你:"现在你可以放心去试了,不用愧疚。"
3. 事后记录而非事前审批
程序不要求你在开始前详细规划预算。你只需要说"我想试试这个",系统检查额度够不够,够了就放行。具体的消耗在结束后如实记录。先放行,后核算——这和"延迟评判"的理念一脉相承。
4. 年末复盘指标
年底程序会生成一份复盘报告:
- 额度使用率(用了多少%)
- 尝试成功率(自己定义的"有收获"的比例)
- 单位时间的体验密度(总愉悦度 ÷ 总耗时)
- 未使用额度的原因分析
最后一项很重要——如果额度没用完,说明你的"浪费许可"还不够充分,明年需要调低门槛。
四、代码模块化实现
项目结构:
trial_quota/
├── main.py # 命令行入口
├── models.py # 数据结构定义
├── quota_manager.py # 额度管理引擎
├── storage.py # 本地持久化
├── data/
│ └── quota.json # 额度与尝试记录
└── README.md
models.py —— 数据结构定义
"""
数据模型层
定义年度额度和试错尝试的结构
"""
from dataclasses import dataclass, field
from typing import Optional
from datetime import datetime, date
import uuid
@dataclass
class AnnualQuota:
"""年度试错额度"""
year: int
total_budget: float = 0.0 # 总预算(元)
total_hours: float = 0.0 # 总时间(小时)
total_projects: int = 0 # 总项目数
used_budget: float = 0.0 # 已用预算
used_hours: float = 0.0 # 已用时间
used_projects: int = 0 # 已启动项目数
@property
def remaining_budget(self) -> float:
return self.total_budget - self.used_budget
@property
def remaining_hours(self) -> float:
return self.total_hours - self.used_hours
@property
def remaining_projects(self) -> int:
return self.total_projects - self.used_projects
@property
def budget_usage_pct(self) -> float:
if self.total_budget == 0:
return 0.0
return round(self.used_budget / self.total_budget * 100, 1)
@property
def hours_usage_pct(self) -> float:
if self.total_hours == 0:
return 0.0
return round(self.used_hours / self.total_hours * 100, 1)
@property
def projects_usage_pct(self) -> float:
if self.total_projects == 0:
return 0.0
return round(self.used_projects / self.total_projects * 100, 1)
def can_afford(self, estimated_budget: float, estimated_hours: float) -> bool:
"""检查是否有足够额度"""
return (
self.remaining_budget >= estimated_budget and
self.remaining_hours >= estimated_hours and
self.remaining_projects >= 1
)
def to_dict(self):
return {
"year": self.year,
"total_budget": self.total_budget,
"total_hours": self.total_hours,
"total_projects": self.total_projects,
"used_budget": self.used_budget,
"used_hours": self.used_hours,
"used_projects": self.used_projects
}
@classmethod
def from_dict(cls, d):
return cls(
year=d["year"],
total_budget=d.get("total_budget", 0),
total_hours=d.get("total_hours", 0),
total_projects=d.get("total_projects", 0),
used_budget=d.get("used_budget", 0),
used_hours=d.get("used_hours", 0),
used_projects=d.get("used_projects", 0)
)
@dataclass
class TrialAttempt:
"""一次试错尝试记录"""
id: str
name: str
description: str = ""
estimated_budget: float = 0.0
estimated_hours: float = 0.0
actual_budget: float = 0.0
actual_hours: float = 0.0
outcome: str = "" # 结果描述
had_fun: Optional[int] = None # 好玩程度 1-5
learned_something: bool = False # 是否有收获
would_do_again: Optional[int] = None # 是否愿意再做 1-5
started_at: datetime = field(default_factory=datetime.now)
finished_at: Optional[datetime] = None
@property
def is_over_budget(self) -> bool:
"""是否超出预估"""
return self.actual_budget > self.estimated_budget or self.actual_hours > self.estimated_hours
@property
def efficiency_score(self) -> Optional[float]:
"""体验效率 = 好玩度 × 是否学到东西 / 实际消耗"""
if not self.had_fun or self.actual_hours == 0:
return None
learning_multiplier = 1.5 if self.learned_something else 1.0
return round(self.had_fun * learning_multiplier / self.actual_hours, 2)
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"estimated_budget": self.estimated_budget,
"estimated_hours": self.estimated_hours,
"actual_budget": self.actual_budget,
"actual_hours": self.actual_hours,
"outcome": self.outcome,
"had_fun": self.had_fun,
"learned_something": self.learned_something,
"would_do_again": self.would_do_again,
"started_at": self.started_at.isoformat(),
"finished_at": self.finished_at.isoformat() if self.finished_at else None
}
@classmethod
def from_dict(cls, d):
return cls(
id=d["id"],
name=d["name"],
description=d.get("description", ""),
estimated_budget=d.get("estimated_budget", 0),
estimated_hours=d.get("estimated_hours", 0),
actual_budget=d.get("actual_budget", 0),
actual_hours=d.get("actual_hours", 0),
outcome=d.get("outcome", ""),
had_fun=d.get("had_fun"),
learned_something=d.get("learned_something", False),
would_do_again=d.get("would_do_again"),
started_at=datetime.fromisoformat(d["started_at"]),
finished_at=datetime.fromisoformat(d["finished_at"]) if d.get("finished_at") else None
)
quota_manager.py —— 额度管理引擎
"""
额度管理引擎
负责额度的设定、检查和扣减
"""
from datetime import datetime
from models import AnnualQuota, TrialAttempt
from typing import List
class QuotaManager:
"""
年度试错额度管理器
核心思想:给浪费一个合法的配额,用纪律保护自由
"""
LICENSE_TEXT = """
╔══════════════════════════════════════════════════════════╗
║ 🧪 试错许可证 / TRIAL LICENSE 🧪 ║
╠══════════════════════════════════════════════════════════╣
║ 项目:{name}
║ 批准时间:{time}
║ 预估消耗:¥{budget} / {hours}h
║ 剩余额度:¥{remaining_budget} / {remaining_hours}h / {remaining_projects}次
║
║ ✅ 在以上额度内,你可以:
║ • 失败,没有关系
║ • 中途放弃,没有关系
║ • 做出毫无用处的东西,没有关系
║ • 纯粹觉得好玩,也没有关系
║
║ 🎯 记住:这不是投资,这是玩耍。
╚══════════════════════════════════════════════════════════╝
"""
def issue_license(self, quota: AnnualQuota, attempt: TrialAttempt) -> str:
"""生成试错许可证"""
return self.LICENSE_TEXT.format(
name=attempt.name,
time=datetime.now().strftime("%Y-%m-%d %H:%M"),
budget=attempt.estimated_budget,
hours=attempt.estimated_hours,
remaining_budget=f"{quota.remaining_budget:.0f}",
remaining_hours=f"{quota.remaining_hours:.0f}",
remaining_projects=quota.remaining_projects
)
def check_and_start(self, quota: AnnualQuota, attempt: TrialAttempt) -> tuple[bool, str]:
"""
检查额度并尝试启动
返回 (是否成功, 消息)
"""
if quota.remaining_projects <= 0:
return False, "❌ 本年度项目额度已用完。明年再来,或者释放一个未完成的尝试。"
if quota.remaining_budget < attempt.estimated_budget:
return False, f"❌ 预算不足。需要 ¥{attempt.estimated_budget},剩余 ¥{quota.remaining_budget:.0f}"
if quota.remaining_hours < attempt.estimated_hours:
return False, f"❌ 时间不足。需要 {attempt.estimated_hours}h,剩余 {quota.remaining_hours:.0f}h"
return True, self.issue_license(quota, attempt)
def finalize(self, quota: AnnualQuota, attempt: TrialAttempt) -> AnnualQuota:
"""完成尝试后扣减额度"""
quota.used_budget += attempt.actual_budget
quota.used_hours += attempt.actual_hours
quota.used_projects += 1
return quota
def rollback(self, quota: AnnualQuota, attempt: TrialAttempt) -> AnnualQuota:
"""取消尝试,退还预估额度(用于误操作后撤销)"""
quota.used_budget -= attempt.estimated_budget
quota.used_hours -= attempt.estimated_hours
quota.used_projects -= 1
return quota
def year_end_report(self, quota: AnnualQuota, attempts: List[TrialAttempt]) -> str:
"""生成年度复盘报告"""
lines = []
lines.append(f"📊 {quota.year} 年度试错额度复盘")
lines.append(f"{'='*50}")
lines.append(f"\n💰 预算:¥{quota.total_budget:.0f} → 已用 ¥{quota.used_budget:.0f} ({quota.budget_usage_pct}%)")
lines.append(f"⏱ 时间:{quota.total_hours:.0f}h → 已用 {quota.used_hours:.0f}h ({quota.hours_usage_pct}%)")
lines.append(f"📦 项目:{quota.total_projects} → 已用 {quota.used_projects} ({quota.projects_usage_pct}%)")
if attempts:
fun_avg = sum(a.had_fun for a in attempts if a.had_fun) / max(1, sum(1 for a in attempts if a.had_fun))
learned_count = sum(1 for a in attempts if a.learned_something)
over_budget = sum(1 for a in attempts if a.is_over_budget)
lines.append(f"\n📈 体验指标:")
lines.append(f" 平均好玩度:{fun_avg:.1f}/5")
lines.append(f" 有收获的项目:{learned_count}/{len(attempts)}")
lines.append(f" 超预估的尝试:{over_budget}/{len(attempts)}")
# 最高效的尝试
efficient = [a for a in attempts if a.efficiency_score]
if efficient:
best = max(efficient, key=lambda x: x.efficiency_score)
lines.append(f"\n🏆 最高效尝试:「{best.name}」效率分:{best.efficiency_score}")
unused_budget = quota.remaining_budget
if unused_budget > quota.total_budget * 0.3:
lines.append(f"\n💡 提示:剩余额度超过30%(¥{unused_budget:.0f}),说明你今年的'浪费许可'还不够充分。明年试试更勇敢一些。")
lines.append(f"\n{'='*50}")
return "\n".join(lines)
storage.py —— 本地持久化
"""
存储模块
所有数据以 JSON 格式保存在本地
"""
import json
from pathlib import Path
from models import AnnualQuota, TrialAttempt
DATA_DIR = Path("data")
QUOTA_FILE = DATA_DIR / "quota.json"
ATTEMPTS_FILE = DATA_DIR / "attempts.json"
def _ensure_files():
DATA_DIR.mkdir(exist_ok=True)
if not QUOTA_FILE.exists():
with open(QUOTA_FILE, "w", encoding="utf-8") as f:
json.dump({}, f)
if not ATTEMPTS_FILE.exists():
with open(ATTEMPTS_FILE, "w", encoding="utf-8") as f:
json.dump([], f)
def load_quota(year: int) -> AnnualQuota | None:
_ensure_files()
with open(QUOTA_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
if str(year) in data:
return AnnualQuota.from_dict(data[str(year)])
return None
def save_quota(quota: AnnualQuota):
_ensure_files()
with open(QUOTA_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
data[str(quota.year)] = quota.to_dict()
with open(QUOTA_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def load_attempts(year: int) -> list[TrialAttempt]:
_ensure_files()
with open(ATTEMPTS_FILE, "r", encoding="utf-8") as f:
all_attempts = [TrialAttempt.from_dict(d) for d in json.load(f)]
return [a for a in all_attempts if a.started_at.year == year]
def save_attempt(attempt: TrialAttempt):
_ensure_files()
with open(ATTEMPTS_FILE, "r", encoding="utf-8") as f:
attempts = json.load(f)
attempts.append(attempt.to_dict())
with open(ATTEMPTS_FILE, "w", encoding="utf-8") as f:
json.dump(attempts, f, ensure_ascii=False, indent=2)
main.py —— 命令行入口
"""
主程序入口
提供命令行交互界面
"""
from datetime import datetime, date
from models import AnnualQuota, TrialAttempt
from quota_manager import QuotaManager
from storage import load_quota, save_quota, load_attempts, save_attempt
def setup_quota():
"""设定年度额度"""
year = int(input("📅 年份:").strip() or date.today().year)
print("\n💰 设定年度试错额度(留白则默认为0):")
try:
budget = float(input(" 预算金额(元):").strip() or "0")
except ValueError:
budget = 0
try:
hours = float(input(" 可用时间(小时):").strip() or "0")
except ValueError:
hours = 0
try:
projects = int(input(" 可启动项目数:").strip() or "0")
except ValueError:
projects = 0
quota = AnnualQuota(
year=year,
total_budget=budget,
total_hours=hours,
total_projects=projects
)
save_quota(quota)
print(f"\n✅ {year} 年度额度已设定。你可以开始合法浪费了。")
return quota
def start_trial(quota: AnnualQuota):
"""开始一次试错"""
print("\n🧪 申请试错许可\n")
name = input(" 项目名称:").strip()
if not name:
print("❌ 名称不能为空")
return None
desc = input(" 描述(可选):").strip()
try:
est_budget = float(input(" 预估花费(元):").strip() or "0")
except ValueError:
est_budget = 0
try:
est_hours = float(input(" 预估耗时(小时):").strip() or "0")
except ValueError:
est_hours = 0
attempt = TrialAttempt(
id=str(int(datetime.now().timestamp()))[-8:],
name=name,
description=desc,
estimated_budget=est_budget,
estimated_hours=est_hours
)
manager = QuotaManager()
ok, msg = manager.check_and_start(quota, attempt)
print(f"\n{msg}")
if ok:
return attempt
return None
def finish_trial(quota: AnnualQuota, attempt: TrialAttempt):
"""完成试错,记录实际消耗"""
print(f"\n📋 结算:「{attempt.name}」\n")
try:
actual_budget = float(input(" 实际花费(元):").strip() or "0")
except ValueError:
actual_budget = 0
try:
actual_hours = float(input(" 实际耗时(小时):").strip() or "0")
except ValueError:
actual_hours = 0
outcome = input(" 结果描述:").strip()
try:
had_fun = int(input(" 好玩程度(1-5):").strip() or "0")
except ValueError:
had_fun = None
learned = input(" 有收获吗?(y/n):").strip().lower() == "y"
try:
again = int(input(" 愿意再做一次吗?(1-5):").strip() or "0")
except ValueError:
again = None
attempt.actual_budget = actual_budget
attempt.actual_hours = actual_hours
attempt.outcome = outcome
attempt.had_fun = had_fun if had_fun else None
attempt.learned_something = learned
attempt.would_do_again = again if again else None
attempt.finished_at = datetime.now()
quota = QuotaManager().finalize(quota, attempt)
save_quota(quota)
save_attempt(attempt)
if attempt.is_over_budget:
print("⚠️ 实际消耗超出了预估。没关系,这就是试错的意义。")
print("✅ 已结算。这次尝试已经成为你的一部分了。")
def show_status(quota: AnnualQuota):
"""显示当前额度状态"""
print(f"\n{'='*50}")
print(f"📊 {quota.year} 年度试错额度状态")
print(f"{'='*50}")
print(f"💰 预算:¥{quota.used_budget:.0f} / ¥{quota.total_budget:.0f} ({quota.budget_usage_pct}%)")
print(f"⏱ 时间:{quota.used_hours:.0f}h / {quota.total_hours:.0f}h ({quota.hours_usage_pct}%)")
print(f"📦 项目:{quota.used_projects} / {quota.total_projects} ({quota.projects_usage_pct}%)")
remaining = quota.remaining_projects
if remaining <= 0:
print(f"\n🚫 项目额度已用完。所有尝试必须在现有资源内完成。")
else:
print(f"\n✅ 剩余 {remaining} 个项目额度,可以继续尝试。")
print(f"{'='*50}")
def year_end_report(quota: AnnualQuota):
"""生成年度复盘"""
attempts = load_attempts(quota.year)
report = QuotaManager().year_end_report(quota, attempts)
print(f"\n{report}")
def main():
print("🧪 年度试错额度管理器")
print("核心理念:给浪费一个合法的配额,用纪律保护自由\n")
year = date.today().year
quota = load_quota(year)
if not quota:
print(f"📭 {year} 年尚未设定额度。")
setup = input("现在设定吗?(y/n):").strip().lower()
if setup == "y":
quota = setup_quota()
else:
print("👋 退出。随时回来设定你的试错额度。")
return
if not quota:
return
pending_attempts = [] # 简化的待完成尝试列表
while True:
show_status(quota)
print("\n请选择操作:")
print("1. 🧪 开始一次试错")
print("2. ✅ 完成并结算")
print("3. 📊 年度复盘报告")
print("4. ⚙️ 重新设定额度")
print("q. 退出")
choice = input("\n> ").strip()
if choice == "1":
attempt = start_trial(quota)
if attempt:
pending_attempts.append(attempt)
# 更新 quota 的剩余(预扣)
quota.used_projects += 1
quota.used_budget += attempt.estimated_budget
quota.used_hours += attempt.estimated_hours
save_quota(quota)
elif choice == "2":
if not pending_attempts:
print("📭 没有进行中的尝试")
continue
print("\n进行中的尝试:")
for i, a in enumerate(pending_attempts):
print(f" {i+1}. {a.name}")
try:
idx = int(input("选择要结算的编号:").strip()) - 1
attempt = pending_attempts.pop(idx)
# 退还预估(finalize 会重新加回实际值)
quota.used_projects -= 1
quota.used_budget -= attempt.estimated_budget
quota.used_hours -= attempt.estimated_hours
finish_trial(quota, attempt)
except (ValueError, IndexError):
print("❌ 无效选择")
elif choice == "3":
year_end_report(quota)
elif choice == "4":
quota = setup_quota()
elif choice == "q":
print("\n👋 再见。愿你今年的每一分浪费,都浪费得心安理得。")
break
else:
print("❌ 无效选择。")
if __name__ == "__main__":
main()
五、README.md 与使用说明
# 年度试错额度管理器(Annual Trial Quota)
一个本地运行的 Python 工具,用于设定年度"合法浪费"配额,在额度内自由开展无收益的创新尝试。
## 设计背景
本项目基于《心理健康与创新能力》课程中的核心观点:
- 拥有"免费实验券"的人表现出更强的探索意愿
- 明确的浪费配额可以降低尝试的心理门槛
- 用纪律保护自由——有限制的玩耍比无限制的焦虑更有效
## 功能特性
- ✅ 设定三维年度额度(预算/时间/项目数)
- ✅ 试错许可证生成(心理授权仪式)
- ✅ 实时额度扣减和余额查看
- ✅ 尝试记录(实际消耗、好玩度、收获感)
- ✅ 年度复盘报告(使用率、效率、超支分析)
- ✅ 完全本地运行,无网络请求,无数据上传
- ✅ 零第三方依赖(仅 Python 标准库)
## 安装与使用
### 环境要求
- Python 3.9+
### 安装
bash
git clone <repository-url>
cd trial_quota
python main.py
### 首次运行
程序会引导你设定当前年份的试错额度。
### 日常使用
#### 设定额度
每年初运行一次,设定三个数字:
- 预算:你愿意"浪费"多少钱(如 500 元)
- 时间:你愿意"浪费"多少小时(如 40 小时)
- 项目:你允许自己启动多少个"可能一无所获"的项目(如 5 个)
#### 开始尝试
选择 `1`,输入项目名称和预估消耗。系统检查额度后生成许可证。
#### 完成结算
选择 `2`,记录实际消耗和体验反馈。
#### 年度复盘
选择 `3`,查看全年试错效率和剩余额度分析。
### 数据文件
- `data/quota.json`:年度额度数据
- `data/attempts.json`:所有尝试记录
## 示例
╔══════════════════════════════════════════════════════════╗
║ 🧪 试错许可证 / TRIAL LICENSE 🧪 ║
╠══════════════════════════════════════════════════════════╣
║ 项目:用 Arduino 做一个自动喂猫器
║ 批准时间:2026-07-28 14:30
║ 预估消耗:¥120 / 8h
║ 剩余额度:¥380 / 32h / 4次
║
║ ✅ 在以上额度内,你可以:
║ • 失败,没有关系
║ • 中途放弃,没有关系
║ • 做
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!