classmethod、getattr、hasattr等6个Python内置方法详解
最近在整理一份Python学习笔记正好写到类和对象这块。前面几篇把类的基本定义、继承、魔法方法都过了一遍这一篇我想单独谈谈类对象和属性的几个内置方法——classmethod、delattr、dir、hasattr、getattr、callable。这几个方法单独拆开看都不难难的是把它们放在真实场景里用顺。很多人学了点语法就扔一边等真写项目的时候要么手动写一堆if判断要么用点号访问属性一旦属性不存在就抛异常代码又丑又脆。这篇笔记就是冲着解决这个问题去的把每个方法的原理、适用场景和坑都捋一遍适合刚学完Python面向对象、想做点实际项目练手的同学。1. 属性访问为什么会乱成一锅粥先看一个真实的反面教材1.1 混乱的起点硬编码字典和一堆散落的工具函数先说说我为什么特意花一篇笔记来写这几个内置方法。前阵子在调试一个内部工具时看到一段让人血压升高的代码一个配置模块里到处是config_dict.get(timeout, 30)、if retry in config_dict、del config_dict[debug]这类写法密钥名写得到处都是改一个键名要全局替换还经常因为键名拼错拿不到配置整个模块维护起来非常痛苦。其实问题不在于字典本身而在于写法没有把属性操作这件事收拢成统一的口令。Python本身提供了一套现成的内置工具专门用来处理对象有没有某个属性怎么取到属性怎么删掉属性怎么发现对象有哪些属性怎么判断一个对象能不能被调用这就是getattr、hasattr、delattr、dir、callable再配合classmethod把类本身的逻辑组织好代码会干净非常多。1.2 把判断-取值-删除-查找拆成四个清晰的口令我后来把那堆散落的字典操作全部改成了对象属性的风格核心就是这四组操作hasattr(obj, name)判断对象有没有某个属性返回布尔值。getattr(obj, name, default)取出属性取不到就返回默认值。delattr(obj, name)删除某个属性。dir(obj)列出对象拥有的属性名列表。单看名字很好记但这几个方法的关系和边界很容易被忽略。比如hasattr内部到底干了什么很多人以为它是直接看一下属性在不在其实不是它会尝试调用getattr(obj, name)并捕获AttributeError。这个细节直接决定了它跟getattr是难兄难弟不是完全独立的两个东西。把属性访问从散落的字典键操作收敛成统一的对象属性方法之后代码的可读性和可维护性都会上一个台阶。接下来逐个拆开看每个方法的本质行为、适用场景以及很容易踩的边界情况。2. classmethod没那么简单它跟 staticmethod 差的不只是第一个参数2.1 先看classmethod的本质行为cls参数到底绑定了什么classmethod是装饰器作用是把一个方法变成类方法。类方法的第一个参数约定俗成叫cls它指向类本身而不是实例。这是它跟实例方法最大的分水岭。class Service: version 2.1.0 classmethod def show_version(cls): return fService version is {cls.version} print(Service.show_version()) # Service version is 2.1.0 print(Service().show_version()) # 实例也能调用类方法输出一样注意第二行实例调用类方法也没有问题Python会自动把实例所属的类传进去。这一点很关键因为很多初学者以为classmethod只能通过类名调用其实实例调用同样有效。那它跟staticmethod的区别在哪静态方法不接收任何隐含的第一个参数它只是一个恰好放在类命名空间里的普通函数。类方法则永远带着cls也就意味着它天然知道自己是谁。2.2 工厂方法与继承体系中的类方法子类调用时发生了什么classmethod最常见的应用场景是工厂方法。比如一个日志处理器接受不同的数据来源我不希望调用方去判断应该创建哪个子类而是让父类提供一个统一的入口返回对应子类的实例。class LogHandler: def __init__(self, source): self.source source classmethod def from_text(cls, text): return cls({type: text, data: text}) classmethod def from_json(cls, raw): return cls({type: json, data: raw}) class FileLogHandler(LogHandler): def __init__(self, source): super().__init__(source) self.source[type] file handler FileLogHandler.from_text(hello) print(type(handler)) # class __main__.FileLogHandler这里的关键是cls。我在基类里写的是cls(...)而不是LogHandler(...)。当子类继承并调用from_text时cls指向的是子类FileLogHandler所以创建出来的实例类型完全正确。如果写死了父类名子类就永远只能用父类实例整个继承体系就废了。这个特性对注册表类的设计也很香。比如某个框架希望插件类注册到一个全局字典里可以在基类里用__init_subclass__或者类方法实现。2.3 三种方法定义的选型对照表我平时写代码时是这样选的直接列个表方便以后查阅方法定义方式第一个参数能否通过实例调用典型用途实例方法self指向实例可以读写实例属性、执行实例级操作classmethodcls指向类可以工厂方法、操作类属性、提供备用构造函数staticmethod无可以跟类和实例都无强关联的工具函数staticmethod并不是没用的但如果你不确定该用哪个classmethod通常是更稳妥的选择因为它保留了类的上下文。什么时候我才用staticmethod只有当前后逻辑真的完全不需要知道类名也不需要访问类属性才考虑它。多一个cls参数有时候能省掉后面好多麻烦。3. getattr、hasattr、delattr属性三件套如何精准配合3.1 getattr带默认值的点号访问比字典.get更高级getattr(obj, name, default)本质上就是用字符串名字去取属性等价于obj.name但多了一个默认值兜底。我常说它是有关键词参数的访问方式。class Config: host 127.0.0.1 port 6379 cfg Config() host getattr(cfg, host, localhost) password getattr(cfg, password, None) print(host) # 127.0.0.1 print(password) # None注意getattr的默认值只在属性不存在时生效如果属性存在但值为None结果就是None不会帮你跳过。这一点跟字典的dict.get(key, default)行为是基本一致的但混用的时候容易产生歧义后面避坑清单里再细说。getattr更适合在动态场景里用。比如从配置字符串里读出属性名再决定取哪个值这时候属性名是运行时才确定的点号语法根本写不了。def read_config(obj, key): return getattr(obj, key, unknown)getattr还有一个硬核特性当属性本身定义了__getattr__魔法方法时访问不到属性会触发它。这样你就可以自定义属性不存在时的行为而不是直接抛异常。这是动态代理、懒加载之类设计的基础。3.2 hasattr的伪结果它只告诉你有没有不告诉你是什么hasattr(obj, name)用来判断对象有没有属性name。但注意它的内部行为是尝试getattr捕获AttributeError。所以如果getattr过程中抛了异常hasattr会把这个异常吞掉返回False。这句话看着简单实际影响很大。当对象实现了__getattr__或者某些属性是property时属性访问可能触发一段逻辑这段逻辑如果内部出错hasattr不会把错误抛出来只会默默返回False。class Wrapper: property def data(self): raise RuntimeError(backend unavailable) w Wrapper() print(hasattr(w, data)) # FalseRuntimeError 被吞掉了新手看到这个很容易懵明明代码里定义了data为什么hasattr返回False真实原因就是property读取时抛了异常。所以在做健壮性检查时hasattr的简单是有代价的它把属性不存在和读取属性时出错混在了一起。3.3 delattr删除属性时的边界行为delattr(obj, name)相当于del obj.name。它也有对应的魔法方法__delattr__删除属性时会触发。delattr最主要的坑在于类属性和实例属性的关系。假设类里定义了一个属性实例又定义了同名属性那实例删除这个属性后访问时会重新看到类属性。class Node: tag base n Node() n.tag instance_tag print(n.tag) # instance_tag delattr(n, tag) print(n.tag) # base类属性还在也就是说删除实例属性并不会删掉类属性只是让实例恢复到从类上找属性。如果你本意是想删除类的某个属性那应该操作类对象delattr(Node, tag)。这两个操作搞混调试起来非常烧脑。3.4 三件套与魔术方法之间的联动我一直建议把这三个方法当成一个整体来理解因为它们背后对应的是__getattr__、__getattribute__、__setattr__、__delattr__这一整套属性访问协议。getattr(obj, name)默认会走__getattribute__如果找不到再走__getattr__。hasattr(obj, name)本质上就是尝试调用getattr捕获异常。delattr(obj, name)会触发__delattr__可以在里面做删除前的校验或日志。理解这层联动关系遇到属性访问怎么不按预期走的问题时就能很快定位出是不是魔术方法影响了行为。后面实战案例里我特意用这些联动关系做了一个能拦截属性删除的类。4. dir与callable动态发现能力才是调试利器4.1 深入dir实例目录和类目录差了什么dir()用来列出对象的属性名。没有参数时列出当前作用域的变量名有参数时列出对象的属性名列表并且按字母排好序。先说一个很容易忽略的区别dir(instance)和dir(Class)的结果是不一样的。实例的目录除了类属性、实例属性还会包含继承链上的属性和特殊方法而类的目录则包含类的属性和方法定义但不包含实例化之后的实例属性。class Demo: class_attr 10 def __init__(self): self.instance_attr 20 def run(self): pass d Demo() print(instance_attr in dir(d)) # True print(instance_attr in dir(Demo)) # False print(class_attr in dir(Demo)) # True在调试的时候dir()比一个个猜属性名高效得多。比如拿到一个陌生的库返回的对象直接dir(obj)看有哪些可用方法再配合help()查签名基本就能摸清用法。还有一个隐藏用法dir()可以配合__dir__魔法方法定制。如果类里实现了__dir__dir(obj)的结果会被它影响这在做一些对外 API 可见性控制时很常用。4.2 callable判断能不能调的底层逻辑callable(obj)接收一个对象返回它能不能被调用。能调用的标志是内部实现了__call__方法。函数、方法、类甚至实现了__call__的实例都是可调用的。def func(): pass class CallableClass: def __call__(self, value): return value * 2 print(callable(func)) # True print(callable(CallableClass)) # True类可调用返回实例 print(callable(CallableClass())) # True实例实现了__call__ print(callable(123)) # False这个方法的用处在于当你写了一个回调分发逻辑不确定传进来的对象到底是函数还是实现了某个接口的实例用callable判断之后就可以大胆调用避免TypeError: xxx object is not callable。callable结合dir还有一个很妙的调试技巧遍历对象的方法列表只挑出可调用的方法看看这个对象到底暴露了哪些动作。action_names [name for name in dir(obj) if callable(getattr(obj, name, None))]这一行能快速筛选出对象可调用的属性对理解一个动态生成的插件接口特别有用。不过要注意dir出来的名字很多都是双下划线开头的魔术方法实际筛选时可以排除它们。5. 综合演练用六个内置方法做一个可动态扩展的任务注册器5.1 需求与设计用数据驱动的方式管理任务学得再多不如动手串一遍。我设计了一个小项目一个任务注册器任务可以通过类方法注册配置外部传入运行前检查任务是否存在运行后还能动态调用。这个项目规模不大但刚好能把classmethod、getattr、hasattr、dir、callable全部用上。核心需求有三个任务类通过classmethod暴露统一的注册入口子类注册时不丢失类型信息。外部按名称调用任务调用前判断任务是否存在。支持列出所有可调用任务方便做诊断和调试。5.2 完整实现与逐段解读直接上代码每段我配合说明。class TaskRegistry: _tasks {} classmethod def register(cls, nameNone): 注册任务。 def decorator(func): task_name name or func.__name__ cls._tasks[task_name] func return func return decorator classmethod def run(cls, task_name, *args, **kwargs): 运行任务任务不存在时返回提示信息。 if not hasattr(cls, _tasks): return fregistry missing: {cls.__name__} tasks getattr(cls, _tasks, {}) if task_name not in tasks: return ftask not found: {task_name} task tasks[task_name] if not callable(task): return ftask not callable: {task_name} return task(*args, **kwargs) classmethod def list_tasks(cls): 列出所有可调用任务。 tasks getattr(cls, _tasks, {}) return { name: callable(func) for name, func in tasks.items() if callable(func) }这段代码里有几个值得细说的点register用了classmethod里面的cls指向调用它的类。子类继承TaskRegistry调用register时cls._tasks会操作子类的类属性吗这里有个经典的继承陷阱如果子类没有单独定义_tasks那它访问的是父类的_tasks所有子类注册的任务会集中在同一个字典里。如果你想要每个子类有自己独立的任务表必须在子类里重新赋值_tasks {}。run方法里我用了hasattr(cls, _tasks)和getattr(cls, _tasks, {})的组合。正常情况下已经有_tasks所以hasattr返回Truegetattr拿到字典。这段兜底逻辑是为了演示当子类没有初始化_tasks时getattr的默认值{}能保证后续代码不崩溃。实际项目里这种先判断再取值的模式非常常用。callable(task)用来确认注册进来的对象确实能调用避免有人注册了一个普通字符串之后调用时抛TypeError。5.3 运行效果与扩展思路下面演示怎么使用这个注册器TaskRegistry.register() def ping(): return pong TaskRegistry.register(nameadd) def add(a, b): return a b print(TaskRegistry.run(ping)) print(TaskRegistry.run(add, 3, 5)) print(TaskRegistry.run(missing_task)) print(TaskRegistry.list_tasks())输出大致是pong 8 task not found: missing_task {ping: True, add: True}如果你想让某个任务类的注册行为更丰富比如校验函数名、记录注册时间可以重写register先调用父类的逻辑再追加额外处理。这种用classmethod搭出来的注册表比写一堆全局变量和if分支要清晰得多。6. 这些坑我替你们踩过了属性内置方法实战避雷清单6.1 hasattr会把property里抛的异常吞成False前面提到过hasattr吞掉AttributeError是为了实现判断属性是否存在但也把其他异常一起吞了。如果在property的 getter 里抛了别的异常hasattr依然返回False这会让你误以为属性不存在。class Circuit: property def status(self): raise ConnectionError(down)解决办法如果你关心属性访问时的异常不要用hasattr直接用getattr包在try/except里捕获具体的异常类型。hasattr适合我只需要知道能不能取到取不到就算了的宽松场景。6.2 getattr的默认值救不了属性存在但值为Nonegetattr(obj, setting, default)的默认值只在属性不存在时生效。如果属性存在但值是None返回的是None不是默认值。class AppConfig: debug None app AppConfig() print(getattr(app, debug, False)) # None不是False这跟dict.get(key, default)是一样的逻辑。做配置回填时如果不想要 None 覆盖默认配置记得先判断返回值是不是 None而不能依赖默认值。很多线上诡异的配置问题都是这么来的。6.3 删除实例属性会露出同名的类属性delattr(instance, name)删除的是实例属性类属性还在。删除之后再用点号访问又会看到类属性值。class Cache: ttl 60 cache Cache() cache.ttl 5 delattr(cache, ttl) print(cache.ttl) # 60如果你要删的是类属性本身必须对类对象操作delattr(Cache, ttl)。否则你会陷入删了还有再删还有的迷茫中。6.4 dir返回列表排过序别依赖它做精确定位dir()返回的列表是排序过的而且包含大量__xxx__魔术方法。直接用dir(obj)找方法通常得在几十个双下划线方法里翻。建议用列表推导式过滤把不关心的__方法去掉method_names [name for name in dir(obj) if not name.startswith(_)]这样列出的才是对外可见的方法做动态方法发现时不容易看花眼。6.5 callable的判断对类永远返回True别把类和实例搞混callable(SomeClass)永远返回True因为调用类会实例化。如果你要做回调分发期望传入的是函数或可调用实例那么类的名字也会被放行。拿到一个类名之后调用就会生成一个实例而不是执行逻辑。所以只靠callable判断还不够真正确认时还得看它的类型或者返回值。比如在注册器里我想区分这个注册项是函数还是类可以加一步import inspect print(inspect.isfunction(TaskRegistry._tasks[ping]))callable是安全调用的第一道门但很多场景下还需要inspect模块做更精确的类型判断。6.6 一个综合排错经验属性访问链路追踪最后分享一个排错思路。当你遇到属性明明存在但访问不到或者删除属性后行为诡异时不要急着改代码按这个顺序排查先dir(obj)确认属性名拼写是否正确。用getattr(obj, name, sentinel)看返回值是否为sentinel判断属性是否真的存在。检查类是否定义了__getattr__或__getattribute__它们会改变属性访问行为。检查实例是否有同名属性类是否有同名属性两者可能混淆。确认property的 getter 是否抛了异常导致hasattr返回False。这套链路我用了很多次基本能把九成属性访问问题定位出来。属性内置方法本身不难难的是在真实项目里快速判断到底是谁在搞鬼。把上面这些行为和边界背下来比死记方法签名有用得多。工具还是那六个关键是理解它们之间的协作方式和隐藏行为。多写几段动态操作的代码遇到几个坑你就能慢慢形成自己的判断节奏了。

相关新闻

基于MySQL的平行志愿模拟录取系统数据库设计与实现

基于MySQL的平行志愿模拟录取系统数据库设计与实现

简介:这份数据库课程设计资源以「平行志愿模拟录取系统」为主题,面向高校计算机相关专业学生及需要完成数据库课程设计的自学者,帮助解决志愿填报、分数排序、投档录取等业务场景下的数据库建模与实现问题。资源包共969个文件,整体…

2026/10/12 6:57:02 阅读更多 →
SSM+JSP网上购物商城项目从零搭建全流程详解

SSM+JSP网上购物商城项目从零搭建全流程详解

如果你正在做Java课程设计或者毕业设计,大概率绕不开“网上购物商城”这个题目。用SSM加JSP这套老组合来做商城,放在今天看依然是一个非常典型、非常能锻炼人的练手项目。这里记录一下我做这类项目时的完整思路,包括模块怎么拆、表怎么建、代…

2026/10/12 6:57:02 阅读更多 →
断网3年、营收反创54亿:老干妈的“离线运营“与“在线制造“实验

断网3年、营收反创54亿:老干妈的“离线运营“与“在线制造“实验

【摘要】社媒停更近3年的老干妈,2025年营收冲上54亿元历史峰值,关键工序产品不良率降至0.02%。拆解其“离线运营在线制造”双轨实践:AI视觉质检系统将拣椒岗位从600个压缩至48个,电商渠道占比不足5%成为收缩线上运营的核心决策依据…

2026/10/12 6:57:02 阅读更多 →

最新新闻

本地ComfyUI生成AI漫剧:从整合包安装到批量出片的完整方案

本地ComfyUI生成AI漫剧:从整合包安装到批量出片的完整方案

前两天一个做短视频的朋友跑来问我:网上那些AI漫剧到底是怎么做出来的?他试了好几个能生成视频的网页,几分钟就烧掉几十块,充值按钮还老在眼前晃,生成次数也卡得死死的。我直接把我自己在用的一套本地方案丢给了他&…

2026/10/12 8:24:00 阅读更多 →
面试六分钟速败?拆解高频变态问题背后的考察逻辑与应对技巧

面试六分钟速败?拆解高频变态问题背后的考察逻辑与应对技巧

10点预约的面试,10点06我已经坐在公司楼下的便利店捧着冰美式发呆,手里攥着被汗水浸皱的简历。前后不到一支烟的时间就结束了战斗,快到我甚至在怀疑自己是不是走错了楼层。但那六分钟里,面试官抛出的问题,每一句都让我…

2026/10/12 8:24:00 阅读更多 →
哈希洪荒攻击:当HashMap变成性能炸弹

哈希洪荒攻击:当HashMap变成性能炸弹

1. 这不是密码学课,是给你讲个能听懂的“快递站爆仓”故事你有没有经历过这种场景:小区门口突然堆满快递,不是因为双十一大促,而是有人用同一张收货单——反复写了1000遍“3栋2单元501”,然后一股脑全塞进快递柜。柜子…

2026/10/12 8:24:00 阅读更多 →
网络最大流算法详解:从EK到Dinic的模板题实战指南

网络最大流算法详解:从EK到Dinic的模板题实战指南

1. 从零理解网络最大流:为什么这道模板题值得反复琢磨很多人第一次在算法题库里刷到“网络最大流”这个标签时,反应都差不多——看了一遍定义,觉得好像懂了,真上手写代码,发现连样例都过不去。我当年也是这样&#xff…

2026/10/12 8:24:00 阅读更多 →
混合发电系统协同调度优化:风电、光热与热电联产建模实战

混合发电系统协同调度优化:风电、光热与热电联产建模实战

凌晨两点,值班室里的风功率预测曲线开始往上翻,光热电站的储热罐温度却已经降到了下限附近,而热电联产机组正被热网指令压着电出力——一边是风电想满发,一边是热用户等着供热,白天攒下来的光热余量还没放干净。这个场…

2026/10/12 8:24:00 阅读更多 →
6G显存也能跑ComfyUI反推与提示词直出:本地部署与避坑指南

6G显存也能跑ComfyUI反推与提示词直出:本地部署与避坑指南

最近折腾 ComfyUI 的花样越来越多,有人拿它做修图,有人拿它跑写真,但我被问得最多的问题是:“只有 6G 显存,能本地玩反推和提示词直出吗?”答案是能,而且比你想象中稳。这篇记录基于我反复装了三…

2026/10/12 8:22:59 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →