当前位置:首页>python>Python教程 Episode 22 - Python 设计模式实战

Python教程 Episode 22 - Python 设计模式实战

  • 2026-08-19 02:07:54
Python教程 Episode 22 - Python 设计模式实战

欢迎来到"Python教程从零基础到实战"系列的第二十二期!

经过了前面二十一期,你已经掌握了 Python 的基础语法、数据结构、面向对象编程、爬虫、数据分析、Web 开发、部署运维和性能优化。但你有没有遇到过这样的代码——

```python

# 一个"功能完善"但结构混乱的配置管理模块

defget_config(key):

"""获取配置"""

# 先读环境变量

    val = os.environ.get(key)

if val:

return val

# 再读配置文件

# ...一堆 if/else...

# 再读数据库...

# ...又一堆逻辑...

return default

defset_config(keyvalue):

# 又要写环境变量、写文件、写数据库...

# 复制粘贴了无数行 get_config 的逻辑

pass

```

这段代码能跑,但维护起来非常痛苦。改一个逻辑要动到处函数,加一个新配置来源要改每一处。问题出在哪?**没有好的架构设计**

这一期,我们来讲——**设计模式**。不照搬 textbook,我们用真实的 Python 项目来说明,每种模式怎么解决实际问题。

---

## 一、为什么需要设计模式?

### 1.1 设计模式的本质

设计模式不是"必须遵守的规则",而是**前辈们总结的、经过验证的解决方案模板**。它们回答的问题是:

> "当 A 情况发生时,用 B 方式来解决,能避免 C 问题。"

比如:**多个类需要共享同一个全局状态**——用**单例模式**,比在每次调用时手动判断"我是不是第一个创建的实例"要优雅得多。

### 1.2 Python 的特殊性

和其他语言不同,Python 有几个独特的优势:

-**一切皆对象**:函数也是对象,可以传递、返回、存储

-**动态特性**:可以在运行时修改类的行为

-**装饰器**:天然支持"在不修改源码的前提下增强功能"

这意味着很多在其他语言里需要用复杂套路实现的模式,在 Python 里有更简单的写法。但这不意味着设计模式没用——它们提供的**结构化思路**依然不可替代。

---

## 二、创建型模式:如何优雅地创建对象

### 2.1 单例模式(Singleton)

**场景**:配置管理器、日志器、数据库连接池——整个程序只需要一个实例。

#### 方案一:模块本身就是单例

```python

# config.py — Python 模块导入天然就是单例!

import os

classConfigManager:

"""配置管理器:确保全局只有一个实例"""

    _instance = None

def__new__(cls):

ifcls._instance isNone:

print("[Config] 创建新实例")

cls._instance = super().__new__(cls)

cls._instance._initialized = False

returncls._instance

def__init__(self):

ifself._initialized:

return# 已经初始化过了,跳过

self._initialized = True

self._config = {}

self._load_from_env()

self._load_from_file()

def_load_from_env(self):

"""从环境变量加载"""

        env_vars = {

"DATABASE_URL": os.getenv("DATABASE_URL"),

"API_KEY": os.getenv("API_KEY"),

"DEBUG": os.getenv("DEBUG""false").lower() == "true",

        }

for key, value in env_vars.items():

if value isnotNone:

self._config[key] = value

def_load_from_file(self):

"""从 YAML 配置文件加载"""

try:

import yaml

withopen("config.yaml""r"encoding="utf-8"as f:

                file_config = yaml.safe_load(f)

if file_config:

self._config.update(file_config)

exceptFileNotFoundError:

pass# 没有配置文件就跳过

exceptImportError:

# yaml 库不存在时使用 JSON

import json

try:

withopen("config.json""r"encoding="utf-8"as f:

self._config.update(json.load(f))

exceptFileNotFoundError:

pass

defget(selfkeydefault=None):

returnself._config.get(key, default)

defset(selfkeyvalue):

self._config[key] = value

def__repr__(self):

returnf"ConfigManager(config={dict(self._config)})"

# 使用

cfg1 = ConfigManager()

cfg2 = ConfigManager()

print(cfg1 is cfg2)  # True! 它们是同一个实例

cfg1.set("APP_NAME""MyApp")

print(cfg2.get("APP_NAME"))  # "MyApp" — 通过另一个实例也能访问

```

#### 方案二:用装饰器实现

```python

from functools import wraps

defsingleton(cls):

"""装饰器版本:适用于任意类"""

    instances = {}

@wraps(cls)

defget_instance(*args, **kwargs):

ifclsnotin instances:

            instances[cls] = cls(*args, **kwargs)

return instances[cls]

return get_instance

@singleton

classDatabaseConnection:

def__init__(selfurlstr):

self.url = url

print(f"[DB] 连接到 {url}")

defquery(selfsqlstr) -> list:

print(f"[DB] 执行 SQL: {sql}")

return [{"id"1"name""Alice"}]

db1 = DatabaseConnection("postgresql://localhost/mydb")

db2 = DatabaseConnection("postgresql://localhost/otherdb")  # ← 被忽略!

print(db1.url)  # "postgresql://localhost/mydb" — 参数只接受第一次的

```

#### 方案三:Pythonic 的做法(推荐)

```python

# 实际上,对于纯配置场景,最 Pythonic 的方式根本不需要单例:

# 直接用模块级别的变量就行

# settings.py

DATABASE_URL = "postgresql://localhost/mydb"

API_KEY = "sk-xxxxx"

DEBUG = False

# 在其他文件中直接导入

from settings importDATABASE_URLDEBUG

# 或者作为命名空间

import settings

print(settings.DATABASE_URL)

```

**经验法则**:Python 中,如果功能能用模块级别变量实现,就别用单例。单例的真正战场在——需要"状态"的场景,比如日志器、指标采集器。

---

### 2.2 工厂模式(Factory)

**场景**:需要根据用户输入或配置,动态选择创建哪种类型的对象。

```python

from abc importABC, abstractmethod

import json

import yaml

classFormatter(ABC):

"""输出格式化器的抽象基类"""

@abstractmethod

defformat(selfdatadict) -> str:

pass

classJSONFormatter(Formatter):

defformat(selfdatadict) -> str:

return json.dumps(data, ensure_ascii=Falseindent=2)

classCSVFormatter(Formatter):

defformat(selfdatadict) -> str:

ifnot data:

return""

        headers = list(data[0].keys())

        lines = [",".join(headers)]

for row in data:

            lines.append(",".join(str(row[h]) for h in headers))

return"\n".join(lines)

classMarkdownTableFormatter(Formatter):

defformat(selfdatadict) -> str:

ifnot data:

return""

        headers = list(data[0].keys())

        lines = ["| " + " | ".join(headers) + " |"]

        lines.append("| " + " | ".join("---"for _ in headers) + " |")

for row in data:

            lines.append("| " + " | ".join(str(row[h]) for h in headers) + " |")

return"\n".join(lines)

# ===== 工厂:负责创建正确的格式化器 =====

classFormatterFactory:

"""集中管理格式化器的创建"""

# 注册表:格式名 -> 格式化器类

    _formatters: dict[strtype] = {

"json": JSONFormatter,

"csv": CSVFormatter,

"markdown": MarkdownTableFormatter,

    }

@classmethod

defregister(clsnamestrformatter_classtype):

"""允许外部注册新的格式化器"""

cls._formatters[name] = formatter_class

@classmethod

defcreate(clsfmtstr) -> Formatter:

"""根据名字创建格式化器"""

if fmt notincls._formatters:

raiseValueError(

f"未知的格式 '{fmt}',支持的格式: {list(cls._formatters.keys())}"

            )

returncls._formatters[fmt]()

# 使用示例

users = [

    {"id"1"name""张三""role""管理员"},

    {"id"2"name""李四""role""普通用户"},

]

# 创建不同格式的输出

for fmt_type in ["json""csv""markdown"]:

    formatter = FormatterFactory.create(fmt_type)

print(f"\n=== {fmt_type.upper()} ===")

print(formatter.format(users))

# 扩展:用户可以注册自己的格式化器

classXMLFormatter(Formatter):

defformat(selfdatadict) -> str:

        lines = ["<?xml version='1.0' encoding='utf-8'?>""<records>"]

for record in data:

            line = "  <record>"

for key, value in record.items():

                line += f"<{key}>{value}</{key}>"

            line += "</record>"

            lines.append(line)

        lines.append("</records>")

return"\n".join(lines)

FormatterFactory.register("xml", XMLFormatter)

xml_formatter = FormatterFactory.create("xml")

print(f"\n=== xml ===\n{xml_formatter.format(users)}")

```

---

### 2.3 建造者模式(Builder)

**场景**:构建一个复杂的 HTTP 请求、构建一份包含多个选项的报告、构建一个数据库查询。

```python

classHttpRequestBuilder:

"""链式调用的 HTTP 请求构建器"""

def__init__(selfmethodstrurlstr):

self.method = method

self.url = url

self._headers: dict[strstr] = {}

self._params: dict[strstr] = {}

self._body = None

defheader(selfkeystrvaluestr) -> "HttpRequestBuilder":

"""添加请求头——返回 self 以支持链式调用"""

self._headers[key] = value

returnself# ← 关键:返回自身

defparams(self, **kwargs) -> "HttpRequestBuilder":

"""添加 URL 查询参数"""

self._params.update(kwargs)

returnself

defjson_body(selfdatadict) -> "HttpRequestBuilder":

"""设置 JSON body"""

import json

self._body = json.dumps(data).encode("utf-8")

self.header("Content-Type""application/json")

returnself

deftext_body(selftextstr) -> "HttpRequestBuilder":

"""设置纯文本 body"""

self._body = text.encode("utf-8")

self.header("Content-Type""text/plain")

returnself

defbuild(self) -> dict:

"""构建最终请求"""

from urllib.parse import urlencode

        request = {

"method"self.method,

"url"self.url,

"headers"self._headers,

"body"self._body,

        }

ifself._params:

            query_string = urlencode(self._params)

            request["url"] += f"?{query_string}"

return request

# ===== 使用示例 =====

# 链式调用构建 GET 请求

get_request = HttpRequestBuilder("GET""https://api.example.com/users").params(

page=1per_page=10

).build()

print("GET 请求:", get_request)

# 链式调用构建 POST 请求

post_request = HttpRequestBuilder("POST""https://api.example.com/users").header(

"Authorization""Bearer my-token"

).json_body({"name""王五""email""wang@example.com"}).build()

print("\nPOST 请求:", post_request)

# 复杂请求

complex_request = (

    HttpRequestBuilder("PUT""https://api.example.com/users/42")

    .header("Authorization""Bearer my-token")

    .header("If-Match"'"v1"')

    .json_body({"name""王五 updated"})

    .build()

)

print("\nPUT 请求:", complex_request)

```

**什么时候用建造者?** 当你需要构建的对象有很多可选参数时,建造者比一堆可选参数的构造函数要清晰得多。

---

## 三、结构型模式:如何优雅地组合对象

### 3.1 适配器模式(Adapter)

**场景**:你的代码想用一个 API,但供应商提供了不同的接口。适配器帮你"翻译"。

```python

from abc importABC, abstractmethod

import smtplib

from email.mime.text import MIMEText

classEmailService(ABC):

"""我们的代码所期望的统一邮件服务接口"""

@abstractmethod

defsend(selftostrsubjectstrbodystr) -> bool:

pass

classSMTPAdapter(EmailService):

"""适配器:将统一接口适配到 SMTP 协议"""

def__init__(selfhoststrportintusernamestrpasswordstr):

self.host = host

self.port = port

self.username = username

self.password = password

defsend(selftostrsubjectstrbodystr) -> bool:

        msg = MIMEText(body, "html""utf-8")

        msg["Subject"] = subject

        msg["From"] = self.username

        msg["To"] = to

try:

            server = smtplib.SMTP(self.host, self.port)

            server.starttls()

            server.login(self.username, self.password)

            server.sendmail(self.username, [to], msg.as_string())

            server.quit()

returnTrue

exceptExceptionas e:

print(f"SMTP 发送失败: {e}")

returnFalse

classSendGridAdapter(EmailService):

"""适配器:将统一接口适配到 SendGrid API"""

def__init__(selfapi_keystr):

self.api_key = api_key

defsend(selftostrsubjectstrbodystr) -> bool:

import requests

        data = {

"personalizations": [{"to": [{"email": to}]}],

"from": {"email""noreply@example.com"},

"subject": subject,

"content": [{"type""text/html""value": body}],

        }

try:

            resp = requests.post(

"https://api.sendgrid.com/v3/mail/send",

headers={

"Authorization"f"Bearer {self.api_key}",

"Content-Type""application/json",

                },

json=data,

timeout=10,

            )

return resp.status_code == 202

exceptExceptionas e:

print(f"SendGrid 发送失败: {e}")

returnFalse

classAWSSESAdapter(EmailService):

"""适配器:适配 AWS SES"""

def__init__(selfregionstraccess_keystrsecret_keystr):

self.region = region

self.access_key = access_key

self.secret_key = secret_key

defsend(selftostrsubjectstrbodystr) -> bool:

try:

import boto3

            ses = boto3.client("ses"region_name=self.region)

            ses.send_email(

Source=self.access_key + "@example.com",

Destination={"ToAddresses": [to]},

Message={

"Subject": {"Data": subject},

"Body": {"Html": {"Data": body}},

                },

            )

returnTrue

exceptExceptionas e:

print(f"AWS SES 发送失败: {e}")

returnFalse

# ===== 使用示例:上层业务代码完全不用关心底层实现 =====

defnotify_user(email_service: EmailService, user_emailstrmessagestr):

"""通知用户——只依赖抽象接口"""

    success = email_service.send(user_email, "系统通知", message)

if success:

print(f"  ✓ 通知已发送至 {user_email}")

else:

print(f"  ✗ 通知发送失败: {user_email}")

# 可以随时切换邮件服务,不需要修改 notify_user 的代码!

smtp_service = SMTPAdapter("smtp.gmail.com"587"user@gmail.com""password")

notify_user(smtp_service, "admin@example.com""<h1>服务器健康</h1><p>一切正常</p>")

# 切换到 SendGrid

sendgrid_service = SendGridAdapter("SG.xxxxx")

notify_user(sendgrid_service, "admin@example.com""<h1>服务器健康</h1><p>一切正常</p>")

```

---

### 3.2 装饰器模式(Decorator)

你已经用过 Python 的 `@` 装饰器语法了,但那是**函数装饰器**——它修改的是函数本身。装饰器模式修饰的是**对象的行为**,两者概念不同但思路相通。

```python

from abc importABC, abstractmethod

classCoffee(ABC):

"""咖啡抽象类"""

@abstractmethod

defcost(self) -> float:

pass

@abstractmethod

defdescription(self) -> str:

pass

classSimpleCoffee(Coffee):

"""基础咖啡"""

defcost(self) -> float:

return10.0

defdescription(self) -> str:

return"简易咖啡"

# ===== 装饰器:给咖啡加料 =====

classCoffeeDecorator(Coffee):

"""咖啡装饰器基类"""

def__init__(selfcoffee: Coffee):

self._coffee = coffee

defcost(self) -> float:

returnself._coffee.cost()

defdescription(self) -> str:

returnself._coffee.description()

classMilk(CoffeeDecorator):

"""牛奶装饰器"""

defcost(self) -> float:

returnself._coffee.cost() + 3.0

defdescription(self) -> str:

returnf"{self._coffee.description()} + 牛奶"

classWhippedCream(CoffeeDecorator):

"""奶油装饰器"""

defcost(self) -> float:

returnself._coffee.cost() + 2.0

defdescription(self) -> str:

returnf"{self._coffee.description()} + 奶油"

classSugar(CoffeeDecorator):

"""糖装饰器"""

defcost(self) -> float:

returnself._coffee.cost() + 1.0

defdescription(self) -> str:

returnf"{self._coffee.description()} + 糖"

# ===== 使用示例 =====

my_coffee = SimpleCoffee()

print(f"{my_coffee.description()}: ¥{my_coffee.cost():.1f}")

# 简易咖啡: ¥10.0

my_coffee = Milk(SimpleCoffee())

print(f"{my_coffee.description()}: ¥{my_coffee.cost():.1f}")

# 简易咖啡 + 牛奶: ¥13.0

my_coffee = WhippedCream(Milk(SimpleCoffee()))

print(f"{my_coffee.description()}: ¥{my_coffee.cost():.1f}")

# 简易咖啡 + 牛奶 + 奶油: ¥15.0

my_coffee = Sugar(WhippedCream(Milk(SimpleCoffee())))

print(f"{my_coffee.description()}: ¥{my_coffee.cost():.1f}")

# 简易咖啡 + 牛奶 + 奶油 + 糖: ¥16.0

```

Python 的函数装饰器和对象装饰器模式可以结合使用:

```python

from functools import wraps

import time

deftimer(func):

"""计时装饰器——测量函数执行时间"""

@wraps(func)

defwrapper(*args, **kwargs):

        start = time.perf_counter()

        result = func(*args, **kwargs)

        elapsed = time.perf_counter() - start

print(f"  [{func.__name__}] 耗时 {elapsed:.4f}s")

return result

return wrapper

defretry(max_retriesint = 3delayfloat = 1.0):

"""重试装饰器——失败时自动重试"""

defdecorator(func):

@wraps(func)

defwrapper(*args, **kwargs):

for attempt inrange(1, max_retries + 1):

try:

return func(*args, **kwargs)

exceptExceptionas e:

if attempt == max_retries:

raise

print(f"  ⚠ 第 {attempt} 次尝试失败: {e}{delay}s 后重试")

                    time.sleep(delay)

return wrapper

return decorator

# 组合使用

@timer

@retry(max_retries=3delay=0.1)

defunstable_api_call(request_idint) -> dict:

"""模拟一个偶尔会失败的 API 调用"""

import random

if random.random() < 0.3:

raiseConnectionError("网络连接不稳定")

return {"request_id": request_id, "data""success"}

result = unstable_api_call(42)

print(f"  结果: {result}")

```

---

### 3.3 外观模式(Facade)

**场景**:子系统太复杂,提供一个简化的统一入口。

```python

import subprocess

import os

from datetime import datetime

classDockerOperations:

"""Docker 操作门面——把复杂的 docker 命令封装成简单方法"""

def__init__(selfproject_dirstr = "."):

self.project_dir = project_dir

defbuild_image(selfimage_namestrtagstr = "latest") -> bool:

"""构建镜像"""

        cmd = f'docker build -t "{image_name}:{tag}" .'

print(f"  🔨 构建镜像: {cmd}")

        result = subprocess.run(cmd, shell=Truecapture_output=Truetext=True,

cwd=self.project_dir)

return result.returncode == 0

defrun_container(selfimage_namestrcontainer_namestr,

portsdict = Nonedetachbool = True) -> bool:

"""运行容器"""

        cmd = f'docker run'

if detach:

            cmd += " -d"

if container_name:

            cmd += f' --name "{container_name}"'

if ports:

for host, container in ports.items():

                cmd += f' -p {host}:{container}'

        cmd += f' "{image_name}:latest"'

print(f"  ▶ 启动容器: {cmd}")

        result = subprocess.run(cmd, shell=Truecapture_output=Truetext=True,

cwd=self.project_dir)

return result.returncode == 0

defstop_container(selfcontainer_namestr) -> bool:

"""停止容器"""

        cmd = f'docker stop "{container_name}"'

print(f"  ⏸ 停止容器: {cmd}")

        result = subprocess.run(cmd, shell=Truecapture_output=Truetext=True)

return result.returncode == 0

defremove_container(selfcontainer_namestr) -> bool:

"""删除容器"""

        cmd = f'docker rm "{container_name}"'

print(f"  🗑 删除容器: {cmd}")

        result = subprocess.run(cmd, shell=Truecapture_output=Truetext=True)

return result.returncode == 0

defhealth_check(selfcontainer_namestr) -> dict:

"""检查容器状态"""

import json

        cmd = f'docker inspect --format="{{{{json .State}}}}" "{container_name}"'

        result = subprocess.run(cmd, shell=Truecapture_output=Truetext=True)

if result.returncode != 0:

return {"status""not_found"}

        state = json.loads(result.stdout)

return {

"running": state.get("Running"False),

"status": state.get("Status""unknown"),

"exit_code": state.get("ExitCode", -1),

"started_at": state.get("StartedAt"""),

        }

# ===== 外观:一行搞定整个部署流程 =====

classDeploymentFacade:

"""部署外观——对外只暴露一个 deploy() 方法"""

def__init__(selfapp_namestrproject_dirstr = "."):

self.app_name = app_name

self.docker = DockerOperations(project_dir)

self.tag = datetime.now().strftime("%Y%m%d-%H%M%S")

defdeploy(selfrestart_if_existsbool = True) -> bool:

"""一键部署:停旧容器 → 删旧容器 → 构建 → 启动"""

        container_name = f"{self.app_name}-app"

# 第一步:清理旧容器

if restart_if_exists:

try:

                health = self.docker.health_check(container_name)

if health["running"]:

print(f"\n  📦 发现运行的容器,先停止...")

self.docker.stop_container(container_name)

self.docker.remove_container(container_name)

exceptException:

pass# 容器不存在也没关系

# 第二步:构建新镜像

        image_name = f"{self.app_name}"

print(f"\n  🏷 标记新版本: {self.tag}")

ifnotself.docker.build_image(image_name, self.tag):

print("  ❌ 镜像构建失败!")

returnFalse

# 第三步:启动容器

print(f"\n  🚀 启动新版本容器...")

ifnotself.docker.run_container(

            image_name, container_name,

ports={8080808080818081}

        ):

print("  ❌ 容器启动失败!")

returnFalse

# 第四步:确认健康

        health = self.docker.health_check(container_name)

if health.get("running"):

print(f"\n  ✅ 部署成功!容器正在运行")

print(f"     访问 http://localhost:8080")

returnTrue

else:

print(f"\n  ⚠️ 容器启动但可能异常: {health}")

returnFalse

# 使用:一行完成部署

facade = DeploymentFacade("my-web-app")

facade.deploy()

```

---

## 四、行为型模式:对象之间如何协作

### 4.1 策略模式(Strategy)

**场景**:同一个操作有多种算法实现,运行时可以选择。比如排序算法、支付渠道、数据导出格式。

```python

from abc importABC, abstractmethod

import csv

import json

import os

classExportStrategy(ABC):

"""导出策略抽象类"""

@abstractmethod

defexport(selfdata: list[dict], filepathstr) -> str:

"""导出数据到指定文件"""

pass

@abstractmethod

defextension(self) -> str:

"""返回文件扩展名"""

pass

classJSONExportStrategy(ExportStrategy):

defexport(selfdata: list[dict], filepathstr) -> str:

withopen(filepath, "w"encoding="utf-8"as f:

            json.dump(data, f, ensure_ascii=Falseindent=2)

return filepath

defextension(self) -> str:

return".json"

classCSVExportStrategy(ExportStrategy):

defexport(selfdata: list[dict], filepathstr) -> str:

ifnot data:

withopen(filepath, "w"encoding="utf-8"as f:

                f.write("")

return filepath

        fieldnames = list(data[0].keys())

withopen(filepath, "w"encoding="utf-8"newline=""as f:

            writer = csv.DictWriter(f, fieldnames=fieldnames)

            writer.writeheader()

            writer.writerows(data)

return filepath

defextension(self) -> str:

return".csv"

classReportGenerator:

"""报告生成器:依赖策略,而非硬编码"""

def__init__(selfstrategy: ExportStrategy):

self._strategy = strategy

defchange_strategy(selfstrategy: ExportStrategy):

"""运行时切换策略"""

self._strategy = strategy

defgenerate_report(selfdata: list[dict], output_dirstr = ".") -> str:

"""生成报告"""

        filepath = os.path.join(

            output_dir, f"report{self._strategy.extension()}"

        )

        exported_path = self._strategy.export(data, filepath)

return exported_path

# 使用

sales_data = [

    {"product""iPhone 15""quantity"120"revenue"899000},

    {"product""MacBook Pro""quantity"45"revenue"2245000},

    {"product""AirPods Pro""quantity"300"revenue"359700},

]

# 默认导出为 JSON

generator = ReportGenerator(JSONExportStrategy())

path = generator.generate_report(sales_data)

print(f"JSON 报告: {path}")

# 切换为 CSV

generator.change_strategy(CSVExportStrategy())

path = generator.generate_report(sales_data)

print(f"CSV 报告: {path}")

```

**核心要点**`ReportGenerator` 完全不关心导出的具体实现。新增一种导出格式只需要创建一个新策略类,不需要改动任何已有代码——符合**开闭原则**(对扩展开放,对修改封闭)。

---

### 4.2 观察者模式(Observer)

**场景**:事件总线、消息推送、UI 刷新、传感器数据订阅。一个对象状态变化时,所有关注者都要收到通知。

```python

classEventBus:

"""简单的事件总线"""

def__init__(self):

self._listeners: dict[strlist] = {}

defsubscribe(selfevent_typestrcallback):

"""订阅某个事件"""

if event_type notinself._listeners:

self._listeners[event_type] = []

self._listeners[event_type].append(callback)

print(f"  👂 订阅: {event_type}")

defunsubscribe(selfevent_typestrcallback):

"""取消订阅"""

if event_type inself._listeners:

self._listeners[event_type].remove(callback)

defpublish(selfevent_typestrdatadict = None):

"""发布事件"""

if data isNone:

            data = {}

if event_type inself._listeners:

for callback inself._listeners[event_type]:

                callback(event_type, data)

# ===== 实际场景:电商系统中的事件通知 =====

classOrderSystem:

"""订单系统——发布事件"""

def__init__(selfbus: EventBus):

self.bus = bus

self._orders = {}

self._order_counter = 0

defcreate_order(selfcustomerstritemslistamountfloat) -> str:

"""创建订单并发布事件"""

self._order_counter += 1

        order_id = f"ORD-{self._order_counter:06d}"

self._orders[order_id] = {

"customer": customer,

"items": items,

"amount": amount,

"status""pending",

        }

# 发布事件

self.bus.publish("order.created", {

"order_id": order_id,

"customer": customer,

"amount": amount,

        })

return order_id

classNotificationService:

"""通知服务——监听事件"""

def__init__(selfbus: EventBus):

        bus.subscribe("order.created"self.on_order_created)

        bus.subscribe("order.paid"self.on_order_paid)

defon_order_created(selfevent_typestrdatadict):

print(f"  📧 [通知服务] 新订单: {data['order_id']} ({data['customer']}, ¥{data['amount']})")

defon_order_paid(selfevent_typestrdatadict):

print(f"  💰 [通知服务] 订单支付成功: {data['order_id']}")

classInventoryService:

"""库存服务——监听事件"""

def__init__(selfbus: EventBus):

        bus.subscribe("order.created"self.on_order_created)

self.stock = {"iPhone 15"100"MacBook Pro"50"AirPods Pro"200}

defon_order_created(selfevent_typestrdatadict):

        total_items = len(data["items"])

print(f"  📦 [库存服务] 新订单占用库存: {total_items} 件商品")

classAnalyticsService:

"""分析服务——监听事件"""

def__init__(selfbus: EventBus):

        bus.subscribe("order.created"self.on_order_created)

        bus.subscribe("order.paid"self.on_order_paid)

self.stats = {"total_orders"0"total_revenue"0.0}

defon_order_created(selfevent_typestrdatadict):

self.stats["total_orders"] += 1

print(f"  📊 [分析服务] 订单计数更新: 总订单={self.stats['total_orders']}")

defon_order_paid(selfevent_typestrdatadict):

self.stats["total_revenue"] += data.get("amount"0)

print(f"  📈 [分析服务] 收入统计更新: 总收入=¥{self.stats['total_revenue']:,.2f}")

# ===== 运行 =====

bus = EventBus()

order_system = OrderSystem(bus)

NotificationService(bus)

InventoryService(bus)

AnalyticsService(bus)

print("\n--- 创建订单 ---")

order_system.create_order(

customer="张三",

items=["iPhone 15""AirPods Pro"],

amount=8590.00,

)

print("\n--- 模拟支付 ---")

bus.publish("order.paid", {"order_id""ORD-000001""amount"8590.00})

```

---

### 4.3 责任链模式(Chain of Responsibility)

**场景**:请求需要经过多道审批/处理步骤。每条链路独立,可以灵活增删。

```python

from abc importABC, abstractmethod

from typing import Optional

classHandler(ABC):

"""处理器抽象类"""

def__init__(self):

self._next_handler: Optional[Handler] = None

defset_next(selfhandler"Handler") -> "Handler":

"""设置下一个处理器,返回它以便链式调用"""

self._next_handler = handler

return handler  # 返回下一个处理器本身

defhandle(selfrequestdict) -> Optional[dict]:

"""处理请求"""

ifself._can_handle(request):

            result = self._process(request)

if result andself._next_handler:

returnself._next_handler.handle(result)

return result

elifself._next_handler:

returnself._next_handler.handle(request)

returnNone

def_can_handle(selfrequestdict) -> bool:

"""判断能否处理此请求——子类覆盖"""

returnTrue

@abstractmethod

def_process(selfrequestdict) -> Optional[dict]:

"""实际处理逻辑——子类覆盖"""

pass

classAuthHandler(Handler):

"""认证处理器"""

def_can_handle(selfrequestdict) -> bool:

return"token"notin request

def_process(selfrequestdict) -> Optional[dict]:

        token = request.get("token")

if token and token.startswith("valid_"):

            request["user"] = "authenticated_user"

print("  🔐 认证成功")

return request

        request["user"] = "anonymous"

print("  🔐 无有效 Token,标记为匿名用户")

return request

classPermissionHandler(Handler):

"""权限处理器"""

REQUIRED_PERMISSIONS = {

"/api/admin""admin",

"/api/orders""reader",

"/api/reports""analyst",

    }

def_can_handle(selfrequestdict) -> bool:

return"user"in request

def_process(selfrequestdict) -> Optional[dict]:

        path = request.get("path""")

        required = self.REQUIRED_PERMISSIONS.get(path)

if required:

print(f"  🔑 路径 {path} 需要权限: {required}")

            request["permission"] = required

return request

classRateLimitHandler(Handler):

"""速率限制处理器"""

def__init__(selfmax_requestsint = 100):

super().__init__()

self.max_requests = max_requests

self._request_count = 0

def_can_handle(selfrequestdict) -> bool:

return"permission"in request

def_process(selfrequestdict) -> Optional[dict]:

self._request_count += 1

ifself._request_count > self.max_requests:

print(f"  ⏱ 速率限制触发!已超过 {self.max_requests} 次请求")

            request["rate_limited"] = True

return request

print(f"  ⏱ 请求计数: {self._request_count}/{self.max_requests}")

return request

classLoggingHandler(Handler):

"""日志处理器(放在链条最后)"""

def_process(selfrequestdict) -> Optional[dict]:

        path = request.get("path""/")

        status = "blocked"if request.get("rate_limited"else"allowed"

        user = request.get("user""unknown")

print(f"  📝 日志: {user} 访问 {path} → {status}")

return request

# ===== 组装责任链 =====

chain = AuthHandler() \

    .set_next(PermissionHandler()) \

    .set_next(RateLimitHandler(max_requests=3)) \

    .set_next(LoggingHandler())

# 测试请求

response = chain.handle({

"path""/api/orders",

"token""invalid_token_xyz",

})

print(f"响应: {response}\n")

# 再来几次请求触发速率限制

for i inrange(4):

    chain.handle({"path""/api/orders""token""valid_user123"})

```

---

## 五、实战重构:从一个"面条代码"到优雅架构

### 5.1 重构前:一团糟

```python

# utils/bad_analyzer.py — 这是一个典型的"什么都在一个文件里"的反面教材

import os

import json

import sqlite3

from datetime import datetime

defprocess_data(input_fileoutput_fileconfig_file="config.json"):

"""这个函数什么都干"""

# 读配置

withopen(config_file, "r"as f:

        config = json.load(f)

    threshold = config.get("anomaly_threshold"0.5)

    db_path = config.get("database""analysis.db")

# 读数据

withopen(input_file, "r"as f:

        data = json.load(f)

# 处理数据

    results = []

for item in data:

        score = 0

if item.get("value"0) > threshold * 100:

            score += 0.3

if item.get("weight"0) > 10:

            score += 0.2

if"flagged"in item.get("tags", []):

            score += 0.5

        result = {

"id": item["id"],

"score": score,

"timestamp": datetime.now().isoformat(),

        }

        results.append(result)

# 存结果

withopen(output_file, "w"as f:

        json.dump(results, f, indent=2)

# 写数据库

    conn = sqlite3.connect(db_path)

    cursor = conn.cursor()

    cursor.execute("""

        CREATE TABLE IF NOT EXISTS analysis_results (

            id TEXT PRIMARY KEY,

            score REAL,

            created_at TEXT

        )

    """)

for r in results:

        cursor.execute(

"INSERT OR REPLACE INTO analysis_results VALUES (?, ?, ?)",

            (r["id"], r["score"], r["timestamp"]),

        )

    conn.commit()

    conn.close()

return results

```

这个函数的毛病:**它同时做了至少五件事**——读配置、读数据、处理分析、写结果文件、写入数据库。任何一件事的变化都会影响其他部分。

### 5.2 重构后:各司其职

```python

# 第一步:配置加载器(单一职责)

classConfigLoader:

"""负责加载和解析配置"""

DEFAULTS = {

"anomaly_threshold"0.5,

"database""analysis.db",

"output_format""json",

    }

def__init__(selfconfig_pathstr = "config.json"):

self.config = dict(self.DEFAULTS)

if os.path.exists(config_path):

withopen(config_path, "r"as f:

self.config.update(json.load(f))

@property

defthreshold(self) -> float:

returnself.config["anomaly_threshold"]

@property

defdb_path(self) -> str:

returnself.config["database"]

# 第二步:数据加载器

classDataLoader:

"""负责从各种源加载数据"""

@staticmethod

defload_json(filepathstr) -> list[dict]:

withopen(filepath, "r"encoding="utf-8"as f:

return json.load(f)

# 第三步:评分引擎(核心算法)

classScoringEngine:

"""计算每项的异常分数"""

def__init__(selfthresholdfloat):

self.threshold = threshold

defscore_item(selfitemdict) -> dict:

"""给单个物品打分"""

        score = 0.0

if item.get("value"0) > self.threshold * 100:

            score += 0.3

if item.get("weight"0) > 10:

            score += 0.2

if"flagged"in item.get("tags", []):

            score += 0.5

return {

"id": item["id"],

"score"round(score, 2),

"timestamp": datetime.now().isoformat(),

        }

# 第四步:结果存储器

classResultStore:

"""存储分析结果"""

def__init__(selfdb_pathstr):

self.db_path = db_path

self._init_db()

def_init_db(self):

import sqlite3

        conn = sqlite3.connect(self.db_path)

        conn.execute("""

            CREATE TABLE IF NOT EXISTS analysis_results (

                id TEXT PRIMARY KEY,

                score REAL,

                created_at TEXT

            )

        """)

        conn.commit()

        conn.close()

defsave_to_file(selfresults: list[dict], filepathstr):

withopen(filepath, "w"encoding="utf-8"as f:

            json.dump(results, f, ensure_ascii=Falseindent=2)

defsave_to_db(selfresults: list[dict]):

import sqlite3

        conn = sqlite3.connect(self.db_path)

for r in results:

            conn.execute(

"INSERT OR REPLACE INTO analysis_results VALUES (?, ?, ?)",

                (r["id"], r["score"], r["timestamp"]),

            )

        conn.commit()

        conn.close()

# 第五步:编排器——把各部分组装起来

classDataAnalyzer:

"""数据分析师——协调各个组件,不做任何具体工作"""

def__init__(selfconfig_pathstr = "config.json"):

self.config = ConfigLoader(config_path)

self.scorer = ScoringEngine(self.config.threshold)

self.store = ResultStore(self.config.db_path)

defanalyze(selfinput_filestroutput_filestr) -> list[dict]:

"""分析入口"""

        data = DataLoader.load_json(input_file)

        results = [self.scorer.score_item(item) for item in data]

self.store.save_to_file(results, output_file)

self.store.save_to_db(results)

return results

```

**重构的收益:**

- 每个类只有**一个存在的理由**(单一职责原则)

- 想换数据存储方式?只需改 `ResultStore`,不动其他代码

- 想改评分规则?只需改 `ScoringEngine`

- 想用不同配置格式?只需换 `ConfigLoader`

- 测试变得超简单:每个类都可以单独单元测试

---

## 六、实操练习

### 练习题

**第 1 题:实现一个简单的缓存装饰器**

基于第 2.1 节学到的装饰器知识,编写一个带 TTL(过期时间)的 LRU 缓存装饰器:

```python

import time

import functools

from typing import Any, Callable

deflru_cache_with_ttl(maxsizeint = 128ttl_secondsint = 300):

"""带过期时间的 LRU 缓存装饰器"""

defdecorator(func: Callable) -> Callable:

        cache = {}

@functools.wraps(func)

defwrapper(*args, **kwargs):

            key = (args, tuple(sorted(kwargs.items())))

if key in cache:

                value, expire_at = cache[key]

if time.time() < expire_at:

return value

            result = func(*args, **kwargs)

            expire_at = time.time() + ttl_seconds

iflen(cache) >= maxsize:

                oldest_key = min(cache, key=lambdak: cache[k][1])

del cache[oldest_key]

            cache[key] = (result, expire_at)

return result

return wrapper

return decorator

@lru_cache_with_ttl(maxsize=64ttl_seconds=60)

deffetch_weather(citystr) -> dict:

print(f"  🌤 正在查询城市: {city}(模拟 API 调用)")

return {"city": city, "temp"22"humidity"65}

# 第一次:调用 API

result1 = fetch_weather("北京")

# 第二次:命中缓存

result2 = fetch_weather("北京")

print(result1)

print(result2)

```

**第 2 题:工厂模式扩展——多数据库支持**

使用第 2.2 节的工厂模式思路,设计一个支持 SQLite、PostgreSQL 两种数据库连接器的工厂类:

```python

from abc importABC, abstractmethod

classDatabase(ABC):

@abstractmethod

defconnect(selfuristr):

pass

@abstractmethod

defquery(selfsqlstr) -> list:

pass

@abstractmethod

defclose(self):

pass

classSQLiteDB(Database):

defconnect(selfuristr):

import sqlite3

self.conn = sqlite3.connect(uri.replace("sqlite:///"""))

returnself

defquery(selfsqlstr) -> list:

returnself.conn.execute(sql).fetchall()

defclose(self):

self.conn.close()

classDatabaseFactory:

    _databases = {

"sqlite": SQLiteDB,

"postgresql"None,  # 需要你补全

    }

@classmethod

defcreate(clsdatabase_typestr) -> Database:

if database_type notincls._databases:

raiseValueError(f"不支持的数据库类型: {database_type}")

returncls._databases[database_type]()

# 使用

db = DatabaseFactory.create("sqlite")

db.connect("sqlite:///test.db")

results = db.query("SELECT 1 + 1")

db.close()

```

**第 3 题:用策略模式实现多种排序算法**

```python

from abc importABC, abstractmethod

import time

import random

classSortStrategy(ABC):

@abstractmethod

defsort(selfdatalist) -> list:

pass

@abstractmethod

defcomplexity(self) -> str:

pass

classBubbleSortStrategy(SortStrategy):

defsort(selfdatalist) -> list:

        arr = data.copy()

        n = len(arr)

for i inrange(n):

for j inrange(0, n - i - 1):

if arr[j] > arr[j + 1]:

                    arr[j], arr[j + 1] = arr[j + 1], arr[j]

return arr

defcomplexity(self) -> str:

return"O(n^2)"

classQuickSortStrategy(SortStrategy):

defsort(selfdatalist) -> list:

iflen(data) <= 1:

return data

        pivot = data[len(data) // 2]

        left = [x for x in data if x < pivot]

        middle = [x for x in data if x == pivot]

        right = [x for x in data if x > pivot]

returnself.sort(left) + middle + self.sort(right)

defcomplexity(self) -> str:

return"O(n log n) 平均"

classSorter:

def__init__(selfstrategy: SortStrategy):

self.strategy = strategy

defset_strategy(selfstrategy: SortStrategy):

self.strategy = strategy

defsort(selfdatalist) -> list:

returnself.strategy.sort(data)

# 对比两种策略

data = [random.randint(110000for _ inrange(10000)]

for name, strategy in [("冒泡", BubbleSortStrategy()), ("快速", QuickSortStrategy())]:

    sorter = Sorter(strategy)

    start = time.perf_counter()

    result = sorter.sort(data)

    elapsed = time.perf_counter() - start

print(f"{name}排序({strategy.complexity()}): {elapsed:.4f}s")

```

**第 4 题:组合模式实现文件系统遍历**

组合模式允许你把"单个对象"和"对象集合"统一对待。请实现一个简单的文件系统操作类:

```python

from abc importABC, abstractmethod

classFileSystemComponent(ABC):

@abstractmethod

defshow_details(selfindentstr = "") -> str:

pass

@abstractmethod

defget_size(self) -> int:

pass

classFile(FileSystemComponent):

def__init__(selfnamestrsizeint):

self.name = name

self.size = size

defshow_details(selfindentstr = "") -> str:

returnf"{indent}📄 {self.name} ({self.size} bytes)"

defget_size(self) -> int:

returnself.size

classDirectory(FileSystemComponent):

def__init__(selfnamestr):

self.name = name

self._children: list[FileSystemComponent] = []

defadd(selfcomponent: FileSystemComponent):

self._children.append(component)

defshow_details(selfindentstr = "") -> str:

        lines = [f"{indent}📁 {self.name}/"]

for child inself._children:

            lines.append(child.show_details(indent + "  "))

return"\n".join(lines)

defget_size(self) -> int:

returnsum(child.get_size() for child inself._children)

# 使用

root = Directory("项目")

src = Directory("src")

src.add(File("main.py"2048))

src.add(File("utils.py"1024))

root.add(src)

root.add(File(".gitignore"128))

print(root.show_details())

print(f"\n总大小: {root.get_size()} bytes")

```

**第 5 题:综合实战——用多个模式搭建一个插件系统**

这是一个综合性题目。请用以下模式设计一个可扩展的插件系统:

-**工厂模式**:创建不同类型的插件

-**策略模式**:每个插件是一个策略

-**观察者模式**:插件可以向事件总线注册回调

-**外观模式**:提供一个简单的 `PluginManager` 类

提示框架:

```python

from abc importABC, abstractmethod

from typing import Callable, Any

classPlugin(ABC):

"""插件基类"""

@abstractmethod

defexecute(selfcontextdict) -> dict:

pass

@abstractmethod

defname(self) -> str:

pass

classEventNotifier:

"""事件通知器"""

def__init__(self):

self._handlers: dict[str, list[Callable]] = {}

defon(selfeventstrhandler: Callable):

self._handlers.setdefault(event, []).append(handler)

deftrigger(selfeventstrdatadict = None):

for handler inself._handlers.get(event, []):

            handler(event, data or {})

classPluginManager:

"""插件管理器——外观模式"""

def__init__(self):

self.notifier = EventNotifier()

self._plugins: dict[str, Plugin] = {}

defregister_plugin(selfplugin: Plugin):

self._plugins[plugin.name()] = plugin

print(f"  ✅ 插件 '{plugin.name()}' 已注册")

defexecute(selfplugin_namestrcontextdict) -> dict:

if plugin_name notinself._plugins:

raiseValueError(f"未找到插件: {plugin_name}")

returnself._plugins[plugin_name].execute(context)

deflist_plugins(self) -> list[str]:

returnlist(self._plugins.keys())

# 定义插件

classEmailPlugin(Plugin):

defname(self) -> str:

return"email"

defexecute(selfcontextdict) -> dict:

        recipient = context.get("recipient""admin@example.com")

print(f"  ✉ 发送邮件至: {recipient}")

return {"status""sent""to": recipient}

classSMSPlugin(Plugin):

defname(self) -> str:

return"sms"

defexecute(selfcontextdict) -> dict:

        phone = context.get("phone""+8613800138000")

print(f"  📱 发送短信至: {phone}")

return {"status""delivered""phone": phone}

# 运行

manager = PluginManager()

manager.register_plugin(EmailPlugin())

manager.register_plugin(SMSPlugin())

print(f"已安装插件: {', '.join(manager.list_plugins())}")

manager.execute("email", {"recipient""user@example.com"})

```

---

## 七、设计模式的"反模式"

在结束之前,我要说一些很重要但不太受欢迎的话:**不是所有地方都需要设计模式。**

### 7.1 常见陷阱

| 陷阱 | 表现 | 建议 |

|------|------|------|

**过度设计** | 一个小脚本里用了 8 个模式 | 用小脚本就该有小脚本的写法 |

**为了模式而模式** | 明明 5 行代码能解决,非要拆成 5 个类 | 先满足功能,再考虑扩展性 |

**滥用继承** | 三层以上的继承树 | Python 优先用组合(composition),少用继承 |

**忽视 Pythonic** | 用 Java 的思维写 Python | Python 有 Python 的习惯用法 |

### 7.2 SOLID 原则速记

设计模式背后有一组更底层的原则,叫做 SOLID:

-**S**ingle Responsibility(单一职责):一个类只做一件事

-**O**pen/Closed(开闭原则):对扩展开放,对修改关闭

-**L**iskov Substitution(里氏替换):子类应该能透明替换父类

-**I**nterface Segregation(接口隔离):接口尽量小

-**D**ependency Inversion(依赖倒置):高层和低层都应依赖抽象

记住:**模式是手段,SOLID 是目的。** 真正重要的不是你用了几个模式,而是你的代码是否易读、易维护、易测试。

---

## 八、总结

这一期的内容比较多,我们系统地学习了:

1.**单例模式**——保证全局唯一实例。但在 Python 中,优先考虑模块级变量。

2.**工厂模式**——解耦对象的创建和使用,便于扩展。

3.**建造者模式**——链式调用构建复杂对象,替代"参数爆炸"。

4.**适配器模式**——让不同接口的组件可以协同工作。

5.**装饰器模式**——给对象添加新功能,与 Python 的 `@decorator` 相辅相成。

6.**外观模式**——简化复杂子系统的一行调用。

7.**策略模式**——运行时切换算法,符合开闭原则。

8.**观察者模式**——事件驱动架构的基础,解耦发布者和订阅者。

9.**责任链模式**——请求逐层处理,每层职责独立。

10.**重构实战**——把一个"面条函数"拆成职责清晰的多个类。

记住最重要的一句话:**好的代码不是用了多少模式,而是让读代码的人一眼就能看懂你在做什么。**

---

## 九、下期预告

**Episode 23:Python 异步编程深度进阶——asyncio 高级用法与实战**

我们已经在前面的章节(Episode 13)介绍了异步编程的基础概念。这一期我们将深入进阶:

- asyncio 事件循环的内部原理

- Task、Future、Semaphore、Queue 的高级用法

- 异步上下文管理器与异步生成器

- 异步 WebSocket 实战

- 如何将现有同步代码安全地异步化

- 常见陷阱:死锁、竞态条件、阻塞调用

敬请期待!

---

**📚 系列回顾**

到目前为止我们学过的内容:

| Episode | 主题 |

|---------|------|

| 01 | 环境搭建与基础语法 |

| 02 | 数据结构与字符串处理 |

| 03 | 面向对象编程 |

| 04 | 装饰器、生成器与文件 IO |

| 05 | 爬虫入门 |

| 06 | 数据分析入门 |

| 07 | Web 后端开发 |

| 08 | AI 实战入门 |

| 09 | 前端入门 |

| 10 | 项目实战——Dashboard |

| 11 | Docker 容器化部署 |

| 12 | 数据库进阶 |

| 13 | 异步编程与并发 |

| 14 | 日志系统与调试技巧 |

| 15 | 自动化运维与脚本实战 |

| 16 | 单元测试与代码质量 |

| 17 | 微服务入门 |

| 18 | CI/CD 与 DevOps 实战 |

| 19 | 并发与多线程深度实践 |

| 20 | 包管理与发布 |

| 21 | 性能优化全指南 |

| 22 | 设计模式实战 ← **本期** |

恭喜你!从零基础到现在,你已经掌握了一套完整的 Python 工程技能链。设计模式是你走向"资深工程师"的关键一步——它教你如何组织代码的结构,而不仅仅是"怎么写对的逻辑"。接下来就看你想拿这套技能去做什么了!💪

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-08-21 22:53:13 HTTP/2.0 GET : https://f.mffb.com.cn/a/506869.html
  2. 运行时间 : 0.283396s [ 吞吐率:3.53req/s ] 内存消耗:5,080.26kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=9f34b399ae71f521e2de28e982028653
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001061s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001659s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000720s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000676s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001302s ]
  6. SELECT * FROM `set` [ RunTime:0.000493s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001423s ]
  8. SELECT * FROM `article` WHERE `id` = 506869 LIMIT 1 [ RunTime:0.001864s ]
  9. UPDATE `article` SET `lasttime` = 1787323993 WHERE `id` = 506869 [ RunTime:0.023710s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000696s ]
  11. SELECT * FROM `article` WHERE `id` < 506869 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001227s ]
  12. SELECT * FROM `article` WHERE `id` > 506869 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001207s ]
  13. SELECT * FROM `article` WHERE `id` < 506869 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.041575s ]
  14. SELECT * FROM `article` WHERE `id` < 506869 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.029845s ]
  15. SELECT * FROM `article` WHERE `id` < 506869 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.013736s ]
0.286525s