Coolify 的 Laravel 最佳实践技能体系:一份按影响面排序的 PHP 后端编码规范
本文以 Coolify 仓库中为 AI Agent 编码场景定制的 Laravel 最佳实践技能文件为核心,系统梳理其"一致性优先"原则、19 类按影响面排序的规则速查表(数据库性能、高级查询、安全、缓存、队列、路由、Eloquent、验证、迁移等),并结合 Coolify 仓库自身的真实配置与源码实现(队列超时参数、ShouldBeUnique 作业、Horizon 配置、encrypted 属性转换)逐条印证这些规则在大型 Laravel 项目中的落地方式,读完可掌握一套可直接用于代码审查与重构的 Laravel 工程化实践清单。
一、这份规范是什么:Coolify 中的 laravel-best-practices 技能
Coolify 是一个基于 Laravel 构建的开源 PaaS 面板,其 composer.json 声明运行环境为 PHP ^8.4、框架 laravel/framework ^12.65.0,是一个典型的现代 Laravel 大型项目。为了让 AI 辅助编码(Claude 技能机制)在该项目中写出的代码风格统一、质量可控,仓库在 .claude/skills/laravel-best-practices/SKILL.md 定义了一份"技能"文档,其元信息声明:
- 触发场景:编写、审查或重构任何 Laravel PHP 代码时均应应用——包括控制器、模型、迁移、Form Request、Policy、Job、定时命令、服务类与 Eloquent 查询;
- 覆盖主题:N+1 与查询性能、缓存策略、授权与安全模式、验证、错误处理、队列与 Job 配置、路由定义、架构决策;
- 许可证:MIT,作者标注为
laravel。
技能文件将规则拆分为 19 个主题,每个主题对应一份独立的规则明细文件(如 rules/db-performance.md、rules/queue-jobs.md 等,均位于 .claude/skills/laravel-best-practices/rules/ 目录),SKILL.md 本身则充当"目录 + 速查表 + 应用流程"的角色。
二、一致性优先:所有规则应用的前提
SKILL.md 开篇强调的原则是 Consistency First(一致性优先):
在应用任何规则之前,先检查应用已经做了什么。Laravel 提供多种有效做法——最佳选择是代码库已经在用的那一种,即使另一种模式理论上更优。不一致比次优模式更糟糕。
具体执行要求:
- 检查同级文件、相关控制器、模型或测试中已确立的模式;
- 若已有既定模式,遵循它,不要引入第二种写法;
- 这些规则只是"尚无既定模式时"的默认值,而不是覆盖已有约定的强制命令。
这一原则在 Coolify 这样的长生命周期项目(自 2023 年起的迁移记录见 database/migrations/)中尤为重要:500+ 次数据库结构演进、48 个控制器、45 个 MCP 工具类,任何"理论上更优"的替换模式如果与周边代码不一致,维护成本反而更高。
三、19 类规则速查表(按影响面排序)
SKILL.md 的 Quick Reference 部分给出了 19 个主题的核心要点。以下完整继承该速查表,并在后续章节对关键主题展开。
| # | 主题 | 明细文件 | 核心要点(速查) |
|---|---|---|---|
| 1 | 数据库性能 | db-performance.md | with() 预加载防 N+1;开发环境启用 Model::preventLazyLoading();只选需要的列;大数据集用 chunk()/chunkById();为 WHERE/ORDER BY/JOIN 列建索引;withCount() 代替加载关系再计数;cursor() 内存高效只读遍历;Blade 模板中禁止查询 |
| 2 | 高级查询模式 | advanced-queries.md | addSelect() 子查询取 has-many 单值;子查询外键 + belongsTo 构造动态关系;CASE WHEN 条件聚合代替多次 count;setRelation() 防循环 N+1;whereIn + pluck() 优于 whereHas;两个简单查询可胜一个复杂查询;与 orderBy 列序一致的复合索引;has-many 排序用相关子查询而非 join |
| 3 | 安全 | security.md | 每个模型定义 $fillable/$guarded,每个动作经 Policy/Gate 授权;用户输入禁入原生 SQL;{{ }} 输出转义、所有 POST/PUT/DELETE 表单 @csrf、认证与 API 路由加 throttle;文件上传校验 MIME/扩展名/大小;.env 永不提交,密钥走 config(),敏感字段用 encrypted 转换 |
| 4 | 缓存 | caching.md | Cache::remember() 代替手动 get/put;Cache::flexible() 实现 stale-while-revalidate;Cache::memo() 避免请求内重复缓存命中;Cache tags 批量失效;Cache::add() 原子条件写入;once() 请求/对象级记忆化;Cache::lock()/lockForUpdate() 处理竞态;生产环境配置 failover 缓存 |
| 5 | Eloquent 模式 | eloquent.md | 关系类型正确并加返回类型提示;局部作用域(local scopes)复用查询约束;全局作用域慎用并记录其存在;属性转换放 casts() 方法;日期列做转换、模板用 Carbon;whereBelongsTo($model) 更清晰;禁止硬编码表名,用 (new Model)->getTable() 或 Eloquent |
| 6 | 验证与表单 | validation.md | 用 Form Request 类而非内联验证;新代码用数组语法 ['required', 'email'],已有约定则跟随;只用 $request->validated(),永不 $request->all();Rule::when() 条件验证;用 after() 代替 withValidator() |
| 7 | 配置 | config.md | env() 只允许出现在配置文件中;用 App::environment() 或 app()->isProduction();配置、语言文件与常量代替硬编码文本 |
| 8 | 测试模式 | testing.md | LazilyRefreshDatabase 快于 RefreshDatabase;assertModelExists() 优于裸 assertDatabaseHas();工厂状态与序列代替手工覆盖;使用 fakes(Event::fake()、Exceptions::fake() 等)——但必须在工厂设置之后而非之前;recycle() 跨工厂共享关系实例 |
| 9 | 队列与 Job | queue-jobs.md | retry_after 必须大于 Job timeout;指数退避 [1, 5, 10];ShouldBeUnique 防重复、ShouldBeUniqueUntilProcessing 提前释放锁;必须实现 failed();配 retryUntil() 时设 $tries = 0;外部 API 调用用 RateLimited 中间件;相关 Job 用 Bus::batch();复杂多队列场景用 Horizon |
| 10 | 路由与控制器 | routing.md | 隐式路由模型绑定;嵌套资源用作用域绑定;Route::resource()/apiResource();方法少于 10 行,业务逻辑抽到 Action/Service;类型提示 Form Request 触发自动验证 |
| 11 | HTTP 客户端 | http-client.md | 每个请求显式 timeout 与 connectTimeout;外部 API 用 retry() + 指数退避;检查响应状态或 throw();独立并发请求用 Http::pool();测试用 Http::fake() 与 preventStrayRequests() |
| 12 | 事件/通知/邮件 | events-notifications.md、mail.md | 事件发现(discovery)优于手动注册,生产 event:cache;事务内用 ShouldDispatchAfterCommit/afterCommit();通知与 Mailable 用 ShouldQueue 走队列;非用户接收者用按需通知(on-demand);可通知模型加 HasLocalePreference;队列化 Mailable 断言 assertQueued() 而非 assertSent();事务性邮件用 Markdown Mailable |
| 13 | 错误处理 | error-handling.md | 在异常类或 bootstrap/app.php 上 report()/render()——跟随既有模式;ShouldntReport 标记不应记录的异常;高频异常限流保护日志端;多捕获场景 dontReportDuplicates();API 路由强制 JSON 渲染;异常类 context() 结构化上下文 |
| 14 | 任务调度 | scheduling.md | 时长不定的任务加 withoutOverlapping();多服务器部署加 onOneServer();长任务并发用 runInBackground();environments() 限定运行环境;takeUntilTimeout() 限时处理;调度组共享配置 |
| 15 | 架构 | architecture.md | 单一职责的 Action 类;依赖注入优于 app() 助手;优先官方 Laravel 包并遵循约定、不要覆盖默认值;默认排序用 ORDER BY id DESC 或 created_at DESC;UTF-8 安全用 mb_*;defer() 做响应后工作、Context 存请求级数据、Concurrency::run() 并行执行 |
| 16 | 迁移 | migrations.md | 用 php artisan make:migration 生成;外键用 constrained();永不修改已在生产运行的迁移;索引在迁移中同步添加,不要事后补;模型 $attributes 镜像列默认值;默认可逆 down(),故意不可逆的变更写前向修复迁移;一个迁移只处理一个关注点,绝不混用 DDL 与 DML |
| 17 | 集合 | collections.md | 简单集合操作用高阶消息;cursor() 与 lazy() 按关系需求选择;遍历时更新记录用 lazyById();集合批量操作用 toQuery() |
| 18 | Blade 与视图 | blade-views.md | 组件模板中 $attributes->merge();Blade 组件优于 @include;每组件脚本用 @pushOnce;共享视图数据用 View Composers;深层嵌套组件属性用 @aware |
| 19 | 约定与风格 | style.md | 所有实体遵循 Laravel 命名约定;优先 Laravel 助手(Str、Arr、Number、Uri、Str::of()、$request->string())而非原生 PHP 函数;Blade 中不写 JS/CSS、PHP 类中不写 HTML;代码应可读,注释仅用于配置文件 |
四、核心规则展开:以 Coolify 源码为证的深度解析
4.1 数据库性能:N+1、分块与索引
db-performance.md 是规则中影响面最大的一类,其要点与标准写法如下。
预加载防 N+1。 懒加载在循环中每行触发一次查询(1 + N 条 SQL),正确做法是 with() 预加载:
// 错误(N+1 — 执行 1 + N 条查询)
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name;
}
// 正确(共 2 条查询)
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name;
}
预加载应进一步约束到只取需要的列(务必保留外键列,否则关系无法匹配):
$users = User::with(['posts' => function ($query) {
$query->select('id', 'user_id', 'title')
->where('published', true)
->latest()
->limit(10);
}])->get();
开发环境主动暴露懒加载。 规则建议在 AppServiceProvider::boot() 中启用:
public function boot(): void
{
Model::preventLazyLoading(! app()->isProduction());
}
访问未预加载的关系时会抛出 LazyLoadingViolationException,让 N+1 在开发期就暴露。这一点在 Coolify 中有直接对应:app/Providers/AppServiceProvider.php 第 43 行存在一行注释掉的 // Model::shouldBeStrict();——从源码结构看,项目评估过严格模式但最终未默认启用,这与"一致性优先"原则下的权衡相符:大型部署面板中部分延迟加载是有意为之的,强制异常可能影响部署流程中的动态属性访问。
分块处理大数据集。 批量处理禁止一次性 all() 加载,chunk() 用于读取,chunkById() 用于迭代中修改记录(标准 chunk() 基于 OFFSET,行变化会导致偏移):
User::where('subscribed', true)->chunk(200, function ($users) {
foreach ($users as $user) {
$user->notify(new WeeklyDigest);
}
});
// 迭代中删除/修改时
User::where('active', false)->chunkById(200, function ($users) {
$users->each->delete();
});
只读的大结果集遍历推荐 cursor()(基于生成器逐行加载):
foreach (User::where('active', true)->cursor() as $user) {
ProcessUser::dispatch($user->id);
}
索引与计数。 为 WHERE、ORDER BY、JOIN、GROUP BY 列建索引,常见查询模式加复合索引(如 WHERE status = ? ORDER BY created_at 对应 $table->index(['status', 'created_at']));计数场景用 withCount(),支持条件计数:
$posts = Post::withCount([
'comments',
'comments as approved_comments_count' => function ($query) {
$query->where('approved', true);
},
])->get();
Blade 中禁止查询。 数据一律由控制器传入,这是规则中明确的红线。
4.2 高级查询:让数据库做它擅长的事
advanced-queries.md 提供了一批"少一条查询就赢"的进阶模式:
addSelect() 相关子查询取单值。 为取 has-many 中"最新时间戳"这类单值,不必预加载整个集合:
public function scopeWithLastLoginAt($query): void
{
$query->addSelect([
'last_login_at' => Login::select('created_at')
->whereColumn('user_id', 'users.id')
->latest()
->take(1),
])->withCasts(['last_login_at' => 'datetime']);
}
扩展该模式可用子查询取外键、再在其上定义 belongsTo,得到"完全水合"的单条关联而无需加载集合:
public function lastLogin(): BelongsTo
{
return $this->belongsTo(Login::class);
}
public function scopeWithLastLogin($query): void
{
$query->addSelect([
'last_login_id' => Login::select('id')
->whereColumn('user_id', 'users.id')
->latest()
->take(1),
])->with('lastLogin');
}
条件聚合代替多次 count。 用 CASE WHEN + toBase() 一条 SQL 完成多个状态计数:
$statuses = Feature::toBase()
->selectRaw("count(case when status = 'Requested' then 1 end) as requested")
->selectRaw("count(case when status = 'Planned' then 1 end) as planned")
->selectRaw("count(case when status = 'Completed' then 1 end) as completed")
->first();
setRelation() 消除循环 N+1。 父模型已加载子集、视图又需要 $child->parent 时,直接注入已加载的父模型:
$feature->load('comments.user');
$feature->comments->each->setRelation('feature', $feature);
whereIn + 子查询优于 whereHas。 whereHas() 生成相关 EXISTS 子查询、逐行重执行;whereIn 配 select('id') 子查询可走索引查找且无 PHP 内存开销:
// 较差(相关 EXISTS 逐行重执行)
$query->whereHas('company', fn ($q) => $q->where('name', 'like', $term));
// 较好(索引友好的子查询)
$query->whereIn('company_id', Company::where('name', 'like', $term)->select('id'));
另外两条经验性规则值得注意:两个简单查询可以胜过一个复杂查询(高选择性的小子查询走自身索引,多一次往返值得);复合索引列序必须与 ORDER BY 一致,否则数据库 filesort。has-many 排序应避免 join(行重复),用 orderBy() 内相关子查询。
4.3 队列与 Job:以 Coolify 生产配置为对照
queue-jobs.md 的规则在 Coolify 仓库中有大量活样本。
retry_after 必须大于 Job timeout。 若配置相反,worker 会在 Job 仍在运行时重新派发它,造成重复执行。Coolify 的 config/queue.php 为 sync 与 redis 连接设置了 'retry_after' => 90,database 连接设置了 'retry_after' => 86400——编写新的长耗时 Job 时,timeout 必须留足小于所在连接的 retry_after 的余量。
指数退避。 public $backoff = [1, 5, 10]; 避免以固定间隔锤打故障的外部服务。
ShouldBeUnique 防重复派发。 Coolify 的三个清理类 Job 都实现了该契约:app/Jobs/CleanupHelperContainersJob.php、app/Jobs/CleanupInstanceStuffsJob.php 与 app/Jobs/CleanupOrphanedPreviewContainersJob.php 均为 implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueue。这正是规则"防重复"意图的直接体现——服务器清理类任务重复执行可能误删资源。规则同时指出:若锁应"开始处理时"而非"完成时"释放,改用 ShouldBeUniqueUntilProcessing(典型场景如更新搜索索引)。
必须实现 failed() 以显式处理错误(更新状态 + 记录带上下文的日志);用 retryUntil() 做时间维度重试限制时必须设 $tries = 0,否则会在时间窗到达前就判定失败。
外部 API 限流与批处理。 调第三方 API 的 Job 应挂 RateLimited 中间件;需要"同成败"的多个 Job 用 Bus::batch() 并配 then()/catch() 回调统一收尾。
Horizon 承担多队列编排。 Coolify 的 config/horizon.php 定义了 s6 监督器:'connection' => 'redis'、队列 'high,default'(可由 HORIZON_QUEUES 覆盖)、'maxJobs' => 400、'memory' => 128,生产环境还配置了按负载自动扩缩的 autoScalingStrategy => 'size' 与 minProcesses/maxProcesses(1 到 4,由环境变量控制)——与规则"复杂多队列、需要监控与自动扩缩时使用 Horizon"完全对应。
4.4 缓存:从 remember 到 failover
caching.md 的要点:
Cache::remember()代替手动 get/put,消除样板代码;Cache::flexible('users', [300, 600], fn () => User::all())实现 stale-while-revalidate:5 分钟内新鲜、10 分钟内"陈旧但可服务",后台延迟刷新,避免高流量键过期瞬间某用户拿到慢响应;Cache::memo()将同一请求内多次读取同一键折叠为一次 Redis 往返;- Cache tags 批量失效相关组(
Cache::tags(['user-1'])->flush()),仅redis、memcached、dynamodb支持,file/database不支持; Cache::add()是原子条件写,取代has()+put()的竞态写法;once()做纯内存的对象/请求级记忆化,不触碰缓存存储;- 生产环境配置 failover 存储:
'failover' => ['driver' => 'failover', 'stores' => ['redis', 'database']],主缓存宕机自动降级。
4.5 安全:Coolify 中可直接观察到的实践
security.md 的规则集在 Coolify 中多处可见实证:
encrypted属性转换。 规则要求对 API 密钥/token 使用encrypted转换并标记hidden。Coolify 的 app/Models/PrivateKey.php 将'private_key' => 'encrypted'列入 casts——SSH 私钥这类高危凭据落库即加密,正是该规则的生产级应用(配套迁移见 database/migrations/2024_09_16_111428_encrypt_existing_private_keys.php,对存量私钥做加密升级)。- API 路由限流。 routes/api.php 中可见
->middleware('throttle:feedback')之类的限流挂载,对应规则"认证与 API 路由加throttle"。 - 授权先行。 规则要求每个动作经 Policy/Gate 授权(控制器中
Gate::authorize('update', $post),或 Form Request 的authorize()返回$this->user()->can(...))。Coolify 仓库中app/Policies/目录下的 27 个策略类(如 app/Policies/ApplicationPolicy.php、app/Policies/ServerPolicy.php)覆盖了核心资源,从源码结构看是"每个动作都授权"规则的大规模落地。 - 其余要点:模型必定义
$fillable(接受用户输入的模型严禁$guarded = []);用户输入永不拼入原生 SQL,必要时用whereRaw('LOWER(name) = ?', [...])绑定参数;Blade 用{{ }}转义、{!! !!}仅限受信已净化内容;所有 POST/PUT/DELETE 表单@csrf;文件上传同时校验扩展名(mimes)与真实 MIME(mimetypes)及大小,落盘用服务器生成的文件名($request->file('avatar')->store('avatars', 'public'));.env永不提交、密钥只经config('services.xxx')读取;CI 中定期跑composer audit审计依赖漏洞。
4.6 路由、控制器与验证
routing.md 的核心是薄控制器 + 自动机制:
// 隐式路由模型绑定
public function show(Post $post)
{
return view('posts.show', ['post' => $post]);
}
// 嵌套资源的作用域绑定
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
// $post 自动被限定在 $user 范围内
})->scopeBindings();
// RESTful 端点
Route::resource('posts', PostController::class);
Route::apiResource('posts', Api\PostController::class); // routes/api.php 中 /api 前缀自动应用
方法目标行数少于 10 行,业务逻辑抽到 Action/Service。Coolify 的目录组织与该规则高度一致:app/Actions/ 下按域划分(Application/、Database/、Server/、Service/ 等)的 Action 类承担业务逻辑,app/Http/Controllers/ 保持转发式薄层——从源码结构看,这正是规则 15"单一职责 Action 类 + 薄控制器"的落地形态。
validation.md 与路由规则协同:类型提示 Form Request 即触发自动验证与授权(方法执行前完成);新代码推荐数组语法 'email' => ['required', 'email', Rule::unique('users')],但已有项目约定优先;数据出口只用 $request->validated(),禁止 $request->all() 参与批量操作;条件规则用 Rule::when($this->account_type === 'business', ['required', 'string', 'max:255']);跨字段的自定义逻辑放 after() 方法而非 withValidator()。
4.7 Eloquent、配置与迁移的边界规则
eloquent.md 的关键边界:
- 关系方法加返回类型提示(
public function comments(): HasMany); - 可复用约束抽局部作用域(
scopeActive(Builder $query): Builder),全局作用域慎用——它会静默改变模型的所有查询(含后台、报表、后台 Job),应只留给软删除、多租户这类真正普适的约束; - 属性转换统一进
casts()方法,日期列必转换、模板直接用 Carbon; - 禁止硬编码表名:
DB::table('users')、字符串 join 与原生 SQL 中的表名字面量都不可取,改用User::where(...)或(new User)->getTable()。唯一例外是迁移——迁移是冻结的快照,模型日后改名或删除不应破坏已运行的迁移,所以迁移内DB::table('settings')可接受且推荐。
config.md 只有一条硬边界:env() 只允许出现在 config/ 文件中,应用代码一律走 config();环境判断用 App::environment() 或 app()->isProduction()。这与 Coolify 的配置实践一致,例如 config/horizon.php 中 env('HORIZON_BALANCE', 'false')、env('HORIZON_QUEUES', 'high,default') 的"环境变量 + 默认值"写法只出现在配置文件里。
migrations.md 的可执行清单:php artisan make:migration 生成;外键 constrained();永不修改已上生产的迁移(Coolify 数百个迁移文件的连续演进史是这一红线最好的注脚);索引随建表加入而非事后补;模型 $attributes 默认值与列默认值镜像;默认写可逆 down(),故意不可逆的变更用前向修复迁移;一个迁移一个关注点,DDL 与 DML 绝不混写。
五、如何应用该技能:SKILL.md 定义的标准流程
SKILL.md 的 "How to Apply" 部分给出了固定工作流(并强调应使用子代理去读取规则文件与探索技能内容):
- 识别文件类型,选择相关章节——例如迁移 → 第 16 节;控制器 → 第 1、3、5、6、10 节;
- 检查同级文件的既有模式——按"一致性优先"原则,既有模式优先于本技能的任何默认规则;
- 用文档检索核对 API 语法——针对当前安装的 Laravel 版本确认精确语法(本文适用前提为 Coolify 所声明的 Laravel 12.x 与 PHP 8.4 环境)。
六、小结
这份 .claude/skills/laravel-best-practices/ 技能的价值在于三层结构的清晰分工:SKILL.md 作为"按影响面排序的速查索引 + 一致性优先的裁决原则 + 三步应用流程",19 个 rules 明细文件提供带错误/正确对照的代码级规则,而 Coolify 仓库本身(ShouldBeUnique 的清理 Job、encrypted 转换的私钥模型、带自动扩缩的 Horizon 配置、retry_after 队列参数、throttle 挂载的 API 路由)则提供了规则在生产级 Laravel 项目中的真实落点。对开发者而言,它既可以作为 AI 辅助编码的约束清单,也可以直接用作人工代码审查与重构的对照检查表——在 Coolify 这类持续演进数年的大型 Laravel 代码库中,"跟随既有模式"与"按影响面取舍规则"往往比追逐理论最优更关键。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00