Python 多态三剑客:鸭子类型、ABC 与 Protocol 的边界与抉择

Pandas入门教程(二十二):股票数据分析实战——yfinance 获取数据、技术指标与收益率分析 把 Pandas 用在真实场景,才能体会它的价值。本文以茅台股票为例,用 yfinance 获取历史行情,完成缺失值处理与列筛选,绘制收盘价曲线,计算 SMA 移动平均线与 RSI 相对强弱指数,并通过日收益率、累计收益率和波动率完成完整的量化分析流程。 阅读详情

Python 多态三剑客:鸭子类型、ABC 与 Protocol 的边界与抉择

“Python 的多态不是一种机制,而是一种哲学——你是什么不重要,你能做什么才重要。”


一、从一个真实困惑说起

你有没有遇到过这种场景:

团队里来了一位 Java 背景的工程师,他写的 Python 代码里到处是 isinstance 检查,每个接口都有显式的抽象基类,继承链层层叠叠。另一位纯 Python 背景的同事则完全相反,函数接受任何对象,只要"能用就行",从不定义接口。

两个人都觉得对方的代码"不对劲",但又说不清楚哪里不对。

这个困惑的根源,是对 Python 多态机制的三种实现方式没有想清楚:鸭子类型、抽象基类(ABC)、Protocol。它们不是同一件事的三种说法,而是三种不同的设计哲学,适用于不同的场景。

这篇文章就来把这三者说透,并回答一个最实际的问题:设计插件系统时,你该选哪一种?


二、鸭子类型:Python 的原生哲学

什么是鸭子类型

“如果它走路像鸭子,叫声像鸭子,那它就是鸭子。”

鸭子类型的核心是:不检查对象的类型,只检查对象是否具备所需的行为。

# 三个完全不相关的类,没有任何共同基类
class Dog:
    def speak(self) -> str:
        return "汪汪"

class Cat:
    def speak(self) -> str:
        return "喵喵"

class Robot:
    def speak(self) -> str:
        return "哔哔"

# 这个函数不关心传入的是什么类型
def make_noise(creature):
    print(creature.speak())

# 全部正常工作
for obj in [Dog(), Cat(), Robot()]:
    make_noise(obj)

没有继承,没有接口声明,只要有 speak 方法,就能用。这是 Python 最自然的多态方式。

鸭子类型的优势与代价

优势很明显:灵活、零耦合、符合 Python 哲学。你可以把任何第三方库的对象传进来,只要它有对应的方法。

但代价也很真实:

def process_file(file_obj):
    # 这个函数期望 file_obj 有 read()、write()、close()
    # 但没有任何地方声明这个期望
    data = file_obj.read()
    # ...
    file_obj.close()

# 传入一个不支持 read() 的对象
process_file({"key": "value"})
# AttributeError: 'dict' object has no attribute 'read'
# 错误在运行时才暴露,而且报错信息不够友好

鸭子类型的问题是:接口契约是隐式的。调用方不知道需要实现什么,错误只在运行时暴露。在小项目里这不是问题,但在 10 人以上的团队、5 万行以上的代码库里,这会成为维护噩梦。


三、抽象基类(ABC):显式契约的力量

ABC 的设计思路

abc 模块让你可以定义必须被子类实现的接口,如果子类没有实现,实例化时就会报错:

from abc import ABC, abstractmethod

class FileProcessor(ABC):
    
    @abstractmethod
    def read(self) -> str:
        """读取内容"""
        ...
    
    @abstractmethod
    def write(self, content: str) -> None:
        """写入内容"""
        ...
    
    def process(self) -> str:
        """模板方法:读取并处理(已有默认实现)"""
        raw = self.read()
        return raw.strip().upper()

# 尝试实例化抽象类——直接报错
# processor = FileProcessor()  
# TypeError: Can't instantiate abstract class FileProcessor

# 子类必须实现所有抽象方法
class LocalFileProcessor(FileProcessor):
    def __init__(self, path: str):
        self.path = path
    
    def read(self) -> str:
        with open(self.path) as f:
            return f.read()
    
    def write(self, content: str) -> None:
        with open(self.path, "w") as f:
            f.write(content)

# 如果子类漏实现了某个方法
class BrokenProcessor(FileProcessor):
    def read(self) -> str:
        return "data"
    # 忘记实现 write

# broken = BrokenProcessor()
# TypeError: Can't instantiate abstract class BrokenProcessor 
# with abstract method write
# 错误在实例化时就暴露,而不是调用时

ABC 的真正价值:模板方法模式

ABC 不只是"强制实现接口",它还支持部分实现——基类可以提供通用逻辑,子类只需实现变化的部分:

from abc import ABC, abstractmethod
import hashlib
import json

class DataSerializer(ABC):
    
    @abstractmethod
    def serialize(self, data: dict) -> bytes:
        """子类实现具体序列化格式"""
        ...
    
    @abstractmethod
    def deserialize(self, raw: bytes) -> dict:
        """子类实现具体反序列化"""
        ...
    
    # 通用逻辑:计算校验和(所有子类共享)
    def checksum(self, data: dict) -> str:
        raw = self.serialize(data)
        return hashlib.md5(raw).hexdigest()
    
    # 通用逻辑:带校验的保存
    def save_with_checksum(self, data: dict) -> tuple[bytes, str]:
        raw = self.serialize(data)
        chk = self.checksum(data)
        return raw, chk

class JsonSerializer(DataSerializer):
    def serialize(self, data: dict) -> bytes:
        return json.dumps(data).encode()
    
    def deserialize(self, raw: bytes) -> dict:
        return json.loads(raw.decode())

class MsgpackSerializer(DataSerializer):
    def serialize(self, data: dict) -> bytes:
        import msgpack
        return msgpack.packb(data)
    
    def deserialize(self, raw: bytes) -> dict:
        import msgpack
        return msgpack.unpackb(raw)

# 两种序列化器都自动拥有 checksum 和 save_with_checksum
s = JsonSerializer()
raw, chk = s.save_with_checksum({"name": "Alice", "age": 30})
print(f"校验和: {chk}")

这是 ABC 最强的场景:有共享逻辑 + 有强制接口要求。

ABC 的局限

ABC 要求显式继承。如果你想让第三方库的类满足你的接口,你没法修改它的继承链:

# 第三方库的类,你无法修改
class ThirdPartyWriter:
    def write(self, content: str) -> None:
        print(f"Writing: {content}")
    
    def read(self) -> str:
        return "third party data"

# 你想让它满足 FileProcessor,但它没有继承 FileProcessor
# isinstance(ThirdPartyWriter(), FileProcessor)  → False
# 你只能写一个适配器,或者让它继承——两者都很麻烦

这就是 Protocol 出现的原因。


四、Protocol:结构子类型的现代解法

Protocol 的核心思想

Python 3.8 引入的 Protocol(PEP 544)实现了结构子类型(structural subtyping):不需要显式继承,只要结构匹配,就认为类型兼容。

from typing import Protocol

class Readable(Protocol):
    def read(self) -> str: ...

class Writable(Protocol):
    def write(self, content: str) -> None: ...

# 组合协议
class ReadWritable(Readable, Writable, Protocol):
    ...

# 第三方类,没有继承任何东西
class ThirdPartyWriter:
    def write(self, content: str) -> None:
        print(f"Writing: {content}")
    
    def read(self) -> str:
        return "data"

# 函数接受 ReadWritable 协议
def process(obj: ReadWritable) -> None:
    data = obj.read()
    obj.write(data.upper())

# mypy 检查:ThirdPartyWriter 结构上满足 ReadWritable,✓
process(ThirdPartyWriter())

mypy 会静态验证 ThirdPartyWriter 是否满足 ReadWritable 协议,不需要任何继承关系。

runtime_checkable:让 Protocol 支持运行时检查

from typing import Protocol, runtime_checkable

@runtime_checkable
class Closeable(Protocol):
    def close(self) -> None: ...

class Connection:
    def close(self) -> None:
        print("连接已关闭")

class Timer:
    def start(self) -> None:
        print("计时开始")
    # 没有 close 方法

conn = Connection()
timer = Timer()

print(isinstance(conn, Closeable))   # True
print(isinstance(timer, Closeable))  # False

# 实用场景:资源清理
def safe_close(resource: object) -> None:
    if isinstance(resource, Closeable):
        resource.close()
    else:
        print(f"{type(resource).__name__} 不支持关闭操作")

注意:runtime_checkable 只检查方法是否存在,不检查签名是否匹配。完整的签名验证还是要靠静态检查工具。


五、静态类型检查与运行时多态如何共存?

这是很多人的困惑点。Python 的类型提示是静态的(编译期/开发期),而鸭子类型是动态的(运行时)。它们真的能和平共处吗?

答案是:完全可以,而且是互补的。

用 Protocol 桥接两个世界

from typing import Protocol, runtime_checkable
from dataclasses import dataclass

@runtime_checkable
class Exportable(Protocol):
    def to_dict(self) -> dict: ...
    def validate(self) -> bool: ...

@dataclass
class UserRecord:
    name: str
    email: str
    
    def to_dict(self) -> dict:
        return {"name": self.name, "email": self.email}
    
    def validate(self) -> bool:
        return "@" in self.email

@dataclass  
class ProductRecord:
    sku: str
    price: float
    
    def to_dict(self) -> dict:
        return {"sku": self.sku, "price": self.price}
    
    def validate(self) -> bool:
        return self.price > 0

# 静态类型检查:mypy 验证参数类型
def export_batch(records: list[Exportable]) -> list[dict]:
    result = []
    for record in records:
        # 运行时多态:实际调用哪个 to_dict 取决于对象类型
        if record.validate():
            result.append(record.to_dict())
    return result

users = [UserRecord("Alice", "alice@example.com"), UserRecord("Bob", "invalid")]
products = [ProductRecord("SKU001", 99.9), ProductRecord("SKU002", -1.0)]

# 静态检查通过,运行时多态正常工作
print(export_batch(users))     # [{'name': 'Alice', 'email': 'alice@example.com'}]
print(export_batch(products))  # [{'sku': 'SKU001', 'price': 99.9}]

静态检查在开发期保证你传入的对象"结构正确",运行时多态决定实际执行哪个实现。两者各司其职。

TypeVar + Protocol:泛型约束

from typing import TypeVar, Protocol

class Comparable(Protocol):
    def __lt__(self, other: "Comparable") -> bool: ...
    def __eq__(self, other: object) -> bool: ...

T = TypeVar("T", bound=Comparable)

def find_min(items: list[T]) -> T:
    """泛型函数:接受任何可比较的列表,返回同类型的最小值"""
    if not items:
        raise ValueError("列表不能为空")
    result = items[0]
    for item in items[1:]:
        if item < result:
            result = item
    return result

# mypy 知道返回类型和输入类型一致
min_int = find_min([3, 1, 4, 1, 5])    # 类型:int
min_str = find_min(["banana", "apple"]) # 类型:str

六、实践案例:插件系统接口定义,选哪一种?

这是最有价值的问题。我们来设计一个真实的插件系统——一个数据导出平台,支持 CSV、Excel、JSON 等多种格式,并允许第三方开发者扩展新格式。

需求分析

需求:
1. 内置 CSV、JSON 导出器
2. 第三方开发者可以编写自己的导出器并注册
3. 导出器需要实现:export()、get_extension()、validate_data()
4. 部分导出器可能需要共享的压缩逻辑
5. 需要在注册时验证插件是否合法

方案设计:ABC + Protocol 组合拳

这是我在实际项目中验证过的最佳方案:用 Protocol 定义最小接口(供第三方实现),用 ABC 定义内置实现的基类(共享逻辑)。

# plugin_interface.py —— 对外暴露的接口定义
from typing import Protocol, runtime_checkable, Any
from pathlib import Path

@runtime_checkable
class ExporterPlugin(Protocol):
    """
    插件接口:第三方开发者实现这个 Protocol 即可
    不需要继承任何东西,结构匹配就行
    """
    
    def export(self, data: list[dict], output_path: Path) -> None:
        """将数据导出到指定路径"""
        ...
    
    def get_extension(self) -> str:
        """返回文件扩展名,如 '.csv'"""
        ...
    
    def validate_data(self, data: list[dict]) -> bool:
        """验证数据是否符合导出要求"""
        ...
# base_exporter.py —— 内置导出器的基类(共享逻辑)
from abc import ABC, abstractmethod
from pathlib import Path
import gzip
import json

class BaseExporter(ABC):
    """
    内置导出器基类:提供共享逻辑
    第三方插件不需要继承这个,只需满足 ExporterPlugin Protocol
    """
    
    def __init__(self, compress: bool = False):
        self.compress = compress
    
    @abstractmethod
    def export(self, data: list[dict], output_path: Path) -> None: ...
    
    @abstractmethod
    def get_extension(self) -> str: ...
    
    def validate_data(self, data: list[dict]) -> bool:
        """默认验证:数据非空且每条记录是字典"""
        return bool(data) and all(isinstance(row, dict) for row in data)
    
    def _write_bytes(self, content: bytes, path: Path) -> None:
        """共享的写入逻辑,支持压缩"""
        if self.compress:
            with gzip.open(str(path) + ".gz", "wb") as f:
                f.write(content)
        else:
            path.write_bytes(content)
# builtin_exporters.py —— 内置实现
import csv
import json
import io
from pathlib import Path
from base_exporter import BaseExporter

class CsvExporter(BaseExporter):
    
    def get_extension(self) -> str:
        return ".csv"
    
    def export(self, data: list[dict], output_path: Path) -> None:
        if not self.validate_data(data):
            raise ValueError("数据验证失败")
        
        buffer = io.StringIO()
        writer = csv.DictWriter(buffer, fieldnames=data[0].keys())
        writer.writeheader()
        writer.writerows(data)
        
        self._write_bytes(buffer.getvalue().encode("utf-8"), output_path)

class JsonExporter(BaseExporter):
    
    def __init__(self, compress: bool = False, indent: int = 2):
        super().__init__(compress)
        self.indent = indent
    
    def get_extension(self) -> str:
        return ".json"
    
    def export(self, data: list[dict], output_path: Path) -> None:
        if not self.validate_data(data):
            raise ValueError("数据验证失败")
        
        content = json.dumps(data, ensure_ascii=False, indent=self.indent)
        self._write_bytes(content.encode("utf-8"), output_path)
# plugin_registry.py —— 插件注册中心
from typing import runtime_checkable
from pathlib import Path
from plugin_interface import ExporterPlugin

class PluginRegistry:
    
    def __init__(self):
        self._plugins: dict[str, ExporterPlugin] = {}
    
    def register(self, name: str, plugin: object) -> None:
        """注册插件,运行时验证是否满足 Protocol"""
        if not isinstance(plugin, ExporterPlugin):
            raise TypeError(
                f"插件 '{name}' 不满足 ExporterPlugin 接口。\n"
                f"需要实现: export(), get_extension(), validate_data()"
            )
        self._plugins[name] = plugin  # type: ignore[assignment]
        print(f"✓ 插件 '{name}' 注册成功 (格式: {plugin.get_extension()})")
    
    def export(self, name: str, data: list[dict], output_path: Path) -> None:
        if name not in self._plugins:
            available = list(self._plugins.keys())
            raise KeyError(f"未知插件 '{name}',可用插件: {available}")
        
        plugin = self._plugins[name]
        plugin.export(data, output_path)
    
    def list_plugins(self) -> list[str]:
        return list(self._plugins.keys())
# 第三方开发者的插件——不需要继承任何东西
class MarkdownExporter:
    """第三方插件:导出为 Markdown 表格"""
    
    def get_extension(self) -> str:
        return ".md"
    
    def validate_data(self, data: list[dict]) -> bool:
        return bool(data)
    
    def export(self, data: list[dict], output_path: Path) -> None:
        if not data:
            return
        
        headers = list(data[0].keys())
        lines = ["| " + " | ".join(headers) + " |"]
        lines.append("| " + " | ".join(["---"] * len(headers)) + " |")
        
        for row in data:
            lines.append("| " + " | ".join(str(row.get(h, "")) for h in headers) + " |")
        
        output_path.write_text("\n".join(lines), encoding="utf-8")


# 使用示例
from builtin_exporters import CsvExporter, JsonExporter

registry = PluginRegistry()
registry.register("csv", CsvExporter())
registry.register("json", JsonExporter(indent=4))
registry.register("markdown", MarkdownExporter())  # 第三方插件,无需继承

data = [
    {"name": "Alice", "age": 30, "city": "北京"},
    {"name": "Bob", "age": 25, "city": "上海"},
]

registry.export("markdown", data, Path("output.md"))
print(registry.list_plugins())  # ['csv', 'json', 'markdown']

为什么是这个组合?

场景                          推荐方案
─────────────────────────────────────────────────────
对外插件接口(第三方实现)      Protocol(无需继承,灵活)
内置实现共享逻辑               ABC(模板方法,代码复用)
运行时插件验证                 runtime_checkable Protocol
静态类型检查                   Protocol + mypy

单独用鸭子类型:注册时无法验证插件合法性,错误在调用时才暴露。
单独用 ABC:第三方插件必须继承你的基类,耦合太强,版本升级时容易破坏兼容性。
Protocol + ABC 组合:对外零耦合,对内有共享逻辑,两全其美。


七、三种方案横向对比

维度              鸭子类型        ABC              Protocol
──────────────────────────────────────────────────────────────
接口声明          隐式            显式(继承)      显式(结构)
第三方兼容        ✓ 天然支持      ✗ 需要继承        ✓ 天然支持
运行时检查        ✗ 无            ✓ 实例化时报错    ✓ isinstance
静态类型检查      ✗ 无            ✓ mypy 支持       ✓ mypy 支持
共享逻辑          ✗ 不支持        ✓ 模板方法        ✗ 不支持
适用场景          小项目/脚本     内部继承体系      插件/开放接口

八、总结

三种多态机制没有绝对的优劣,只有适不适合:

鸭子类型是 Python 的灵魂,适合小范围、快速迭代的场景,代价是接口契约隐式,维护成本随规模上升。

ABC 适合内部继承体系,当你需要共享逻辑 + 强制接口时,它是最自然的选择。但它要求显式继承,对第三方扩展不友好。

Protocol 是现代 Python 的答案,结合了鸭子类型的灵活性和静态检查的安全性。设计对外接口、插件系统、跨库兼容层时,它是首选。

在真实的大型项目里,这三者往往同时存在:Protocol 定义边界,ABC 实现内部,鸭子类型处理边缘情况。理解它们各自的边界,才能在正确的地方用正确的工具。


互动讨论

你在设计插件系统或模块接口时,遇到过哪些"接口设计后悔了"的经历?是继承链太深难以修改,还是鸭子类型导致运行时才发现问题?

另一个值得思考的问题:随着 Python 3.12 对泛型语法的简化,Protocol 的使用门槛会进一步降低。你认为未来 ABC 的使用场景会被 Protocol 逐步取代吗?

欢迎在评论区分享你的实战经验。


参考资料

服务器被黑?快速检测和识别系统中的恶意进程 在管理服务器时,检测和识别系统中的恶意进程至关重要。本文介绍了使用 `ps`、`top`、`htop` 等系统监控工具,分析日志文件,以及使用 `chkrootkit` 和 `rkhunter` 等安全工具的方法。通过这些步骤,可以有效地检测和处理恶意进程,确保系统的安全和稳定。 阅读详情

相关推荐

【FreeRTOS】FreeRTOS 源码学习笔记 (3)队列 及 xQueueGenericSend分析

引言 本篇又不写调度器了,准备先整理整理“队列“,无他,就觉得这个很有用,自己写应用层代码的时候,队列要用的比较多,之前一直用一个自己写的环形buffer,里面nReadIndex,nWriteIndex,nFreeIndex来管理总觉得不够“官方”,这次看到一个如此成熟的队列,还是忍不住先分析一下。 ...

YoYo的桃子树 5104

Python抽象类(ABC)浅析

今天在看apscheduler源代码中,看到abc.py这个文件,出于好奇,深入看了一下。 abc的缩写是Abstract Base Classes,翻译就是抽象基类。详细链接可以查看此处 可以看出这个是类是2007年,由Python创始人Guido van Rossum和Talin一起引入的。 引入目的: 重载isinstance()和issubclass()。 增加新模块abc...

夜深人静后的码农 2521

量子城域网系列(六):从星型到环形,解析QKD网络拓扑的演进与选型

本文深入解析了量子密钥分发网络从星型到环形等主流拓扑结构的演进历程与核心原理。针对量子城域网的实际部署,文章详细对比了星型、总线型和环形拓扑的优缺点,并结合政务云、金融网点等典型应用场景,提供了实战选型指南,帮助读者在物理限制、安全需求与成本效率间找到最佳平衡。

weixin_29197835的博客 111

学习笔记(46):Python实战编程-protocol

您观看课程学习后 免费入群领取【超全Python资料包+17本学习电子书】         当你需要一门语法简单功能强悍的编程语言时,Python一定是开发者的首选,经过Google多年以来不断的完善,使得Python成为了今天世界上最流行的编程语言之一,同时由于各个开源...

python_AI_fans的博客 3188

医学影像处理实战:如何用Python+SimpleITK搞定DICOM/NIfTI重采样(附完整代码)

本文详细介绍了如何使用Python和SimpleITK库实现医学影像(DICOM/NIfTI格式)的智能重采样技术。通过实战代码演示,帮助读者掌握医学图像处理中的关键步骤,包括环境搭建、数据准备、核心算法实现和质量验证,解决医学AI领域中的影像标准化难题。

linux的博客 981

Jetson AGX Orin平台Jetpack5升级Jetpack6,自研载板USB不工作问题调试

1. 问题描述将AGX Orin工业的新板放到定制的载板上当在ubuntu 22和Jetpack 6.1上使用SDKManager刷机时,lsusb为空,并且在引导时没有发现/注册xusb设备。USB C端口正常打开并供电,但似乎没有数据传输。在Ubuntu 18 Jetpack 5.1.4 SDKManager上同样刷机之后,usb C集线器在引导时被正确地发现,显示在lsusb中,并识别/挂载外部驱动器。

jzwjzw19900922的博客 1509

AI全栈实战 | 3.9-01 爬虫入门:requests 加 BeautifulSoup 三分钟跑通,但 90% 的人栽在这 4 个“礼貌“问题上

⚠️:本文内容仅用于。请遵守目标站点的服务条款与相关法律法规,不得对目标服务造成负担,不得采集、传播个人隐私数据。技术是中性的,边界在你手里。爬虫的入门技术(requests 发请求 + BeautifulSoup 解析)三分钟就能学会。真正区分"会用爬虫"和"会做爬虫的人"的,是这篇我们跑通"请求→解析→存储"的最小闭环,然后重点把"合规与礼貌"这根弦给你绷上。

源码获取 | 运行部署 | 定制二开修改,如果回复不及时请耐心等待,请见谅。 405

Python全栈入门到实战【JavaScript篇 15】async/await与Fetch API,打通前后端数据通道

Fetch API是浏览器内置的现代HTTP请求接口,用于向服务器发送请求并获取响应。它是(XHR)的替代品。学习Fetch的目标非常明确——用JS从后端API获取数据,并渲染到前端页面。这标志着前后端数据通道的打通,是实现全栈开发的核心技能。try {// 等待Promiseconst parallel = await Promise.all([ // 并行等待]);

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 1980

从一段视频到可漫游的 4D 有声场景:思路拆解与最小复现手记

只需一段手机视频,你就能生成可"走进去"的 4D 有声场景:转头时狗叫声方向随之变化,走近时声音更清晰。本文拆解 Video-to-4DAVScene 的核心思路,手把手带你用几十行 Python 搭出最小管线,把视频中的运动物体变成带空间位置的声源,并附上三个真实踩坑的排障经验,代码可直接跑。🎧

helloagent的博客 234

《网络安全自学教程》- 防火墙原理分析与Windows防火墙使用配置

防火墙分类及原理,Windows开启防火墙,Windows防火墙添加策略,

wangyuxiang946的博客 1万+

深度学习-155-RAG技术之嵌入模型Embedding怎么选

Embedding技术在大型语言模型中发挥着至关重要的作用。它将文本转化为向量,使模型能够理解和处理复杂的语言信息。通过选择合适的Embedding方法和优化文本匹配策略,我们可以在搜索引擎、问答系统、推荐系统等领域实现高效且精准的文本匹配。

qq_20466211的博客 405

【每天一个 Python 小案例30】Python 基础知识综合小项目:我的工具盒

作为系列收官,本篇把前 29 篇的知识点合成一个能直接用的命令行工具"我的工具盒":计算器(用 ast 解析表达式,不碰 eval)、单位换算与温度转换(一张换算表 + 特例分支)、记账与分类统计(Decimal + JSON 持久化)、以及用"字典当命令表"把菜单和业务逻辑解耦的主循环。文中对比了 eval 的安全隐患与 ast 白名单的做法,解释了 lambda 闭包在字典里的写法与晚绑定陷阱,并在最后给出整个系列的知识地图,把变量、数据结构、控制流、函数、类、异常、文件、标准库八块内容对应到具体篇目。

xinjiyaya的博客 426

Qt C++ 电子海图与作战叠加(Overlay)系统

lab_pos->setText(QString("坐标: X%1 Y%2").arg(p.x(),0,'f',0).arg(p.y(),0,'f',0));lab_distance->setText(QString("距离: %1 海里").arg(dist,0,'f',1));lab_bearing->setText(QString("方位: %1°").arg(bear,0,'f',1));// 绘制S-57海图基底。scene->addText("封锁线").setPos(450,180);

寒寒豆豆软件公司的博客 496

Python+Agent入门笔记(二):列表推导式、模块和异常,这些写 Agent 前得会

接上一篇《Python+Agent入门笔记(一)》。基础语法跑通之后,这篇聊几块日常写脚本绕不开的东西。还是按 JS/Go 开发者的视角写,尽量记录我实际卡过的地方。

qq_41241612的博客 430

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

详解Python条件语句

在 Python 中,if...else 是一种条件语句,可以根据给定的条件执行不同的操作。这个语句通常用于控制程序的流程。

2601_96798737的博客 205

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

Python3 协程:异步编程的核心机制与实战

协程是解决I/O密集型任务等待瓶颈的高效机制,相比线程更轻量、无锁开销。从yield到async/await的演进,使异步编程语义清晰、易于理解。通过async/await可实现并发执行多个任务(如gather、create_task),显著提升网络请求、爬虫等场景性能。关键在于使用异步库(如aiohttp)替代同步库,避免阻塞事件循环。合理控制超时与并发数,才能发挥协程最大优势。

Lxn2zh的博客 159

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

python如何控制浏览器

Python 可以通过 Selenium、Playwright 等工具实现浏览器自动化,从打开网页到执行复杂交互操作均可脚本化完成。Selenium生态成熟

2601_96797912的博客 76

torch.compile 差异仅 1e-6,边界样本预测为何翻转

torch.compile 加速后输出与 eager 不一致,部分预测翻转。逐层对比与 graph break 分析定位到动态分支翻转根因。容差回归仅适用于无分支翻转场景,零容忍场景应禁用编译。

昨日种种,皆成今我。今日种种,皆成新我。 159

二进制木马免杀-Python 分离免杀与 Web 挂马

二进制木马免杀-Python 分离免杀与 Web 挂马

2503_94637398的博客 268

Python 100讲课程导航:第08篇《Python input 与类型转换:构建可靠的交互程序》

本文详解Python中input()的返回类型为字符串,强调必须通过int()、float()等函数进行类型转换。重点讲解了输入验证的重要性,结合try-except处理ValueError等异常,实现可靠的交互程序。通过购物预算计算器实战,演示如何防范非法输入、处理边界值,并推荐使用Decimal避免浮点精度问题,提升程序健壮性。

铭渊老黄 134

【Python】国家中小学智慧教育平台电子教材批量下载与 PDF 合成实战

成果:两本语文电子课本(一年级上册 118 页 / 23MB、三年级上册 130 页 / 30.3MB),合计 248 页、约 54MB,单本下载加合成 40~60 秒,PDF 页数校验全部通过。经验教训SPA 站点的数据接口优先从 bundle 里挖,比抓页面稳得多;资源链接要按 private/public 分组看待,只取平台公开部分;页数、目录名这类“可推断”的信息,永远用实际响应验证一次;受限环境里“能不能跑浏览器”要在方案定稿前确认。后续计划:这版 PDF 是图片版,没有文字层;

木头大左的博客 134

魔塔社区微调训练Qwen2-1.5B-Instruct模型并烧录到‌RK3568开发板教程

本文基于Ubuntu 22.04 + CUDA 13.0.3环境,使用LLaMA-Factory框架对Qwen2-1.5B-Instruct模型进行LoRA微调,训练儿童故事生成专用模型。通过配置qwen模板、制作alpaca格式数据集、执行sft训练并合并LoRA权重,最终将模型转换为GGUF格式,并量化为Q4_K_M以适配RK3568嵌入式平台。全程支持国产镜像加速下载与显存优化,实现高效轻量化部署。

浅清秋慕夏的博客 186

【回眸】DeepSeek V4.1 Flash 快速上手与实战

本文介绍如何高效本地部署开源大模型,通过现代化工具链实现分钟级环境搭建。聚焦显存优化、一键安装、代码调用与多轮对话实现,解决“显存不足”“连接超时”等痛点,提供参数调优与结构化输出技巧,助力开发者在离线场景下快速构建稳定、高效的本地推理系统。

啤酒鸭的物联网开发记录博客 223

智叟云集-手机证据抓取系统的工程结构与应用入口

本文系统阐述手机证据抓取系统的工程结构与应用入口,涵盖四大层面:工程目录划分(Android、Python服务、Web页面、测试与构建工具);Android组件通过Manifest声明可启动页面,主界面由MainActivity管理;Python服务通过run.py解析参数并启动FastAPI应用,支持远程访问;浏览器页面采用HTML/CSS/JS分离架构,静态资源通过路径关联,实现动态交互。各模块分工明确,协同构建可运行的采集系统。

2301_77178996的博客 232

Java Arrays.asList 常见坑详解|UnsupportedOperationException、数组联动、基本类型 bug

解析 Arrays.asList 常见开发陷阱,演示 UnsupportedOperationException、底层数组引用共享、基本类型数组问题,区分 Arrays 内部 ArrayList 与 java.util.ArrayList,给出数组转 List 的各种场景正确代码。

weixin_42617033的博客 470

利用python中matplotlib.pyplot库绘制图

plt.plot(x,y) # x,y是每个点的坐标,plot()把每个点按顺序画折线。plt.subplots(2,2)表示绘制2*2的子图布局,axs[0,0]表示在第0行第0列绘制子图...coordinates_i[:,0]表示获取每一行(每一个spot)的第0列(x坐标)coordinates_i[:,1]表示获取每一行(每一个spot)的第0列(y坐标)plot.subplot(2,2,1) 表示子图布局是2*2的,开始绘制第一个。也可以导入数据绘制。可以设置color属性,设置颜色。

qq_46147236的博客 102
上一篇: Python 类型提示:约束、文档还是设计工具?高级工程师的真实答案
下一篇: Python 泛型全攻略:从工程价值到 TypeVar 实战抽象,提升代码复用与可维护性
铭渊老黄
铭渊老黄 新星创作者: Python技术领域 新星创作者: Python技术领域
博客等级 码龄7年 1万+粉丝 2218原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭渊老黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值