从菱形继承到 `super()`:彻底理解 Python MRO 与多继承方法查找机制

从菱形继承到 super():彻底理解 Python MRO 与多继承方法查找机制

在 Python 编程中,多继承既强大又危险。它能让我们把多个类的能力组合起来,也可能让代码变得难以理解。很多开发者第一次接触下面这种代码时,都会困惑:

python 复制代码
class A:
    def run(self):
        print("A.run")


class B(A):
    def run(self):
        print("B.run")


class C(A):
    def run(self):
        print("C.run")


class D(B, C):
    pass


d = D()
d.run()

问题来了:d.run() 到底调用 B.run,还是 C.run,还是 A.run

答案是:

python 复制代码
B.run

为什么?这就要理解 Python 的 MRO


一、什么是 MRO?

MRO,全称是 Method Resolution Order ,中文通常翻译为 方法解析顺序

它决定了当我们访问一个方法或属性时,Python 会按照什么顺序在类和父类中查找。

例如:

python 复制代码
print(D.__mro__)

输出类似:

python 复制代码
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

这说明 D 的查找顺序是:

text 复制代码
D -> B -> C -> A -> object

所以当执行:

python 复制代码
d.run()

Python 会先看 D 有没有 run,没有;再看 B,找到了,于是调用 B.run

也可以使用:

python 复制代码
print(D.mro())

得到同样的信息。


二、为什么 Python 需要 MRO?

在单继承中,方法查找很简单:

python 复制代码
class Animal:
    def speak(self):
        print("animal")


class Dog(Animal):
    pass


Dog().speak()

查找顺序就是:

text 复制代码
Dog -> Animal -> object

但多继承会复杂得多。

python 复制代码
class Flyer:
    def move(self):
        print("fly")


class Swimmer:
    def move(self):
        print("swim")


class Duck(Flyer, Swimmer):
    pass


duck = Duck()
duck.move()

这里 Duck 同时继承了 FlyerSwimmer。那 move() 应该来自谁?

答案取决于继承顺序:

python 复制代码
class Duck(Flyer, Swimmer):
    pass

MRO 是:

text 复制代码
Duck -> Flyer -> Swimmer -> object

所以调用:

python 复制代码
duck.move()

输出:

python 复制代码
fly

如果写成:

python 复制代码
class Duck(Swimmer, Flyer):
    pass

则查找顺序变成:

text 复制代码
Duck -> Swimmer -> Flyer -> object

输出就是:

python 复制代码
swim

这说明:多继承中,父类声明顺序会影响方法查找结果。


三、菱形继承:MRO 真正发挥作用的地方

多继承最经典的问题是 菱形继承

python 复制代码
class A:
    def process(self):
        print("A.process")


class B(A):
    def process(self):
        print("B.process")


class C(A):
    def process(self):
        print("C.process")


class D(B, C):
    pass


print(D.__mro__)
D().process()

继承关系可以表示为:

text 复制代码
      A
     / \
    B   C
     \ /
      D

这就是所谓的菱形继承。

在 Python 3 中,输出为:

python 复制代码
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
B.process

也就是说,Python 不会简单粗暴地重复查找 A,而是通过一套算法生成稳定、合理、不重复的查找顺序。

这套算法叫 C3 线性化算法


四、C3 线性化:Python 如何解决多继承冲突?

Python 采用 C3 线性化来计算 MRO。它遵循几个核心原则:

text 复制代码
1. 子类永远优先于父类
2. 父类之间的声明顺序要被尊重
3. 每个类在 MRO 中只出现一次
4. 查找顺序必须保持一致性

以前面的代码为例:

python 复制代码
class D(B, C):
    pass

Python 会尊重 D(B, C) 中的顺序,所以 B 要排在 C 前面。

同时,BC 都继承自 A,但 A 只能出现一次,并且要排在 BC 后面。

因此最终得到:

text 复制代码
D -> B -> C -> A -> object

这就是为什么 D().process() 会优先调用 B.process()


五、MRO 不只影响方法,也影响属性

虽然 MRO 名字里有 "Method",但它不只影响方法查找,也影响类属性查找。

python 复制代码
class A:
    value = "A"


class B(A):
    value = "B"


class C(A):
    value = "C"


class D(B, C):
    pass


print(D.value)
print(D().__class__.mro())

输出:

python 复制代码
B
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

因为 D 自己没有 value,Python 按照 MRO 去 B 中找,找到了 B.value,于是停止查找。

这也是为什么理解 MRO 对排查属性覆盖问题非常重要。


六、super() 和 MRO 的关系

很多人误以为:

python 复制代码
super()

的意思是"调用父类"。

更准确地说,super() 是:

text 复制代码
按照当前类的 MRO,调用下一个类的方法

这点非常重要。

看一个例子:

python 复制代码
class A:
    def handle(self):
        print("A.handle")


class B(A):
    def handle(self):
        print("B.handle")
        super().handle()


class C(A):
    def handle(self):
        print("C.handle")
        super().handle()


class D(B, C):
    def handle(self):
        print("D.handle")
        super().handle()


d = D()
d.handle()

输出:

python 复制代码
D.handle
B.handle
C.handle
A.handle

很多初学者会疑惑:B 的父类明明是 A,为什么 B 里的 super().handle() 调用了 C.handle()

因为对于 D 的实例来说,MRO 是:

text 复制代码
D -> B -> C -> A -> object

当执行到 B.handle() 里的 super() 时,它不是机械地找 B 的父类 A,而是沿着 D 的 MRO,找到 B 后面的下一个类,也就是 C

所以:

python 复制代码
super()

不是"父类",而是"下一个"。


七、协作式多继承:让每个类都有机会执行

MRO 和 super() 配合,能够实现一种非常优雅的设计方式:协作式多继承

python 复制代码
class Base:
    def save(self):
        print("Base.save")


class ValidateMixin(Base):
    def save(self):
        print("Validate data")
        super().save()


class LogMixin(Base):
    def save(self):
        print("Write log")
        super().save()


class UserService(ValidateMixin, LogMixin):
    def save(self):
        print("UserService.save")
        super().save()


service = UserService()
service.save()

输出:

python 复制代码
UserService.save
Validate data
Write log
Base.save

MRO 是:

python 复制代码
print(UserService.__mro__)

结果:

text 复制代码
UserService -> ValidateMixin -> LogMixin -> Base -> object

这里每个类都调用了 super().save(),所以调用链能够完整地传递下去。

这就是协作式多继承的核心:每个类只负责自己的逻辑,然后把后续流程交给 MRO 中的下一个类。


八、Mixin:多继承最常见的实战方式

在 Python 项目中,多继承最常见的使用方式不是构建复杂的父子关系,而是使用 Mixin

Mixin 可以理解为"能力插件"。

比如:

python 复制代码
class JsonMixin:
    def to_json(self):
        import json
        return json.dumps(self.__dict__, ensure_ascii=False)


class ReprMixin:
    def __repr__(self):
        fields = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
        return f"{self.__class__.__name__}({fields})"


class User(JsonMixin, ReprMixin):
    def __init__(self, name, age):
        self.name = name
        self.age = age


u = User("Alice", 18)

print(u)
print(u.to_json())

输出:

python 复制代码
User(name='Alice', age=18)
{"name": "Alice", "age": 18}

这里 JsonMixin 提供 JSON 序列化能力,ReprMixin 提供友好的打印能力,User 通过多继承组合这些能力。

Mixin 的设计建议:

text 复制代码
Mixin 尽量小而专注
Mixin 不应该保存复杂状态
Mixin 最好不要单独实例化
Mixin 命名通常以 Mixin 结尾
多个 Mixin 之间尽量减少隐式依赖

好的 Mixin 像乐高积木,职责清晰,组合灵活。


九、实战案例:权限、日志、缓存的组合

假设我们正在开发一个后台服务,需要在查询用户数据时加入权限检查、日志记录和缓存。

可以这样设计:

python 复制代码
class BaseQuery:
    def query(self, user_id):
        print(f"Query database for user {user_id}")
        return {"id": user_id, "name": "Alice"}


class PermissionMixin:
    def query(self, user_id):
        print("Check permission")
        return super().query(user_id)


class LogMixin:
    def query(self, user_id):
        print(f"Log query: user_id={user_id}")
        return super().query(user_id)


class CacheMixin:
    _cache = {}

    def query(self, user_id):
        if user_id in self._cache:
            print("Hit cache")
            return self._cache[user_id]

        print("Miss cache")
        result = super().query(user_id)
        self._cache[user_id] = result
        return result


class UserQueryService(PermissionMixin, LogMixin, CacheMixin, BaseQuery):
    pass


service = UserQueryService()

print(service.query(1))
print("---")
print(service.query(1))

输出类似:

python 复制代码
Check permission
Log query: user_id=1
Miss cache
Query database for user 1
{'id': 1, 'name': 'Alice'}
---
Check permission
Log query: user_id=1
Hit cache
{'id': 1, 'name': 'Alice'}

查看 MRO:

python 复制代码
print(UserQueryService.__mro__)

结果:

text 复制代码
UserQueryService
-> PermissionMixin
-> LogMixin
-> CacheMixin
-> BaseQuery
-> object

这段代码的关键点是:每个 Mixin 都调用 super().query(user_id),让调用链继续向后传递。

如果某个 Mixin 忘记调用 super(),后面的逻辑就会被截断。


十、常见错误一:把 super() 当作固定父类调用

错误理解:

python 复制代码
super() == 父类

正确理解:

python 复制代码
super() == MRO 中的下一个类

例如:

python 复制代码
class A:
    def run(self):
        print("A")


class B(A):
    def run(self):
        print("B")
        super().run()


class C(A):
    def run(self):
        print("C")
        super().run()


class D(B, C):
    pass


D().run()

输出:

python 复制代码
B
C
A

这正是 MRO 的效果。


十一、常见错误二:多继承中不统一方法签名

协作式多继承要求多个类的方法签名尽量兼容。

不推荐:

python 复制代码
class A:
    def setup(self, config):
        print("A setup")


class B:
    def setup(self):
        print("B setup")


class C(A, B):
    pass

这种情况下,调用 super() 很容易出现参数不匹配。

更推荐使用统一签名,或者使用 *args, **kwargs

python 复制代码
class A:
    def setup(self, **kwargs):
        print("A setup")
        super().setup(**kwargs)


class B:
    def setup(self, **kwargs):
        print("B setup")
        super().setup(**kwargs)


class Base:
    def setup(self, **kwargs):
        print("Base setup")


class C(A, B, Base):
    pass


C().setup(config={"debug": True})

输出:

python 复制代码
A setup
B setup
Base setup

在大型框架中,这种设计非常常见。


十二、常见错误三:继承顺序写反

父类顺序会直接影响 MRO。

python 复制代码
class AuthMixin:
    def dispatch(self):
        print("auth")
        super().dispatch()


class LogMixin:
    def dispatch(self):
        print("log")
        super().dispatch()


class View:
    def dispatch(self):
        print("view")


class MyView(AuthMixin, LogMixin, View):
    pass


MyView().dispatch()

输出:

python 复制代码
auth
log
view

如果写成:

python 复制代码
class MyView(LogMixin, AuthMixin, View):
    pass

输出就变成:

python 复制代码
log
auth
view

这在 Web 框架中尤其重要。比如认证、限流、日志、缓存这些逻辑,顺序不同,系统行为就可能完全不同。


十三、MRO 冲突:Python 也会拒绝不合理继承

有些继承关系会让 Python 无法生成一致的 MRO。

python 复制代码
class A:
    pass


class B:
    pass


class C(A, B):
    pass


class D(B, A):
    pass


class E(C, D):
    pass

这段代码会报错:

python 复制代码
TypeError: Cannot create a consistent method resolution order

原因是:

text 复制代码
C 要求 A 在 B 前面
D 要求 B 在 A 前面
E 同时继承 C 和 D

这两个要求互相矛盾,所以 Python 无法生成一个合理的 MRO。

这也是 C3 线性化的价值:它不会在模糊和冲突中随便选择一个结果,而是直接告诉你继承设计有问题。


十四、调试 MRO 的实用方法

在项目中遇到多继承问题,可以按下面步骤排查。

1. 打印 MRO

python 复制代码
print(MyClass.__mro__)

或者:

python 复制代码
print(MyClass.mro())

这是最直接的方法。

2. 查看方法来自哪个类

python 复制代码
print(MyClass.method)
print(MyClass.__dict__.get("method"))

如果当前类没有,再去 MRO 中逐个查找:

python 复制代码
for cls in MyClass.__mro__:
    if "method" in cls.__dict__:
        print(cls, cls.__dict__["method"])

3. 在每个方法中打印当前类

python 复制代码
class A:
    def run(self):
        print("A.run")
        super().run()

这种方式简单粗暴,但在排查调用链时非常有效。

4. 检查是否有人忘记调用 super()

在协作式多继承中,只要中间某个类没有调用 super(),后面的类就不会被执行。


十五、最佳实践:如何安全使用多继承?

1. 优先使用组合,而不是继承

很多时候,与其写:

python 复制代码
class Service(AuthMixin, CacheMixin, LogMixin):
    pass

不如显式组合:

python 复制代码
class Service:
    def __init__(self, auth, cache, logger):
        self.auth = auth
        self.cache = cache
        self.logger = logger

继承表达"是什么",组合表达"有什么能力"。业务系统中,组合往往更清晰。

2. 多继承适合表达横向能力

适合用 Mixin 的能力包括:

text 复制代码
序列化
日志
权限校验
缓存
重试
格式转换
通用校验

不适合用多继承堆叠复杂业务身份。

3. 所有协作类都调用 super()

只要你希望调用链继续传递,就必须调用:

python 复制代码
super().method()

并且保持方法签名兼容。

4. 控制继承层级

继承链太深时,MRO 会变得难以理解。一般来说,业务代码中继承层级越浅越好。

5. 写测试覆盖调用顺序

对于依赖 MRO 的代码,建议写测试明确调用顺序。

python 复制代码
def test_mro_order():
    assert UserQueryService.mro()[:4] == [
        UserQueryService,
        PermissionMixin,
        LogMixin,
        CacheMixin,
    ]

这能避免后续重构时父类顺序被无意改动。


十六、一张图总结 MRO 查找过程

当执行:

python 复制代码
obj.method()

Python 大致会这样查找:
#mermaid-svg-9dWnk6ArTqbbaOVN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9dWnk6ArTqbbaOVN .error-icon{fill:#552222;}#mermaid-svg-9dWnk6ArTqbbaOVN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9dWnk6ArTqbbaOVN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9dWnk6ArTqbbaOVN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9dWnk6ArTqbbaOVN .marker.cross{stroke:#333333;}#mermaid-svg-9dWnk6ArTqbbaOVN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9dWnk6ArTqbbaOVN p{margin:0;}#mermaid-svg-9dWnk6ArTqbbaOVN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster-label text{fill:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster-label span{color:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster-label span p{background-color:transparent;}#mermaid-svg-9dWnk6ArTqbbaOVN .label text,#mermaid-svg-9dWnk6ArTqbbaOVN span{fill:#333;color:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN .node rect,#mermaid-svg-9dWnk6ArTqbbaOVN .node circle,#mermaid-svg-9dWnk6ArTqbbaOVN .node ellipse,#mermaid-svg-9dWnk6ArTqbbaOVN .node polygon,#mermaid-svg-9dWnk6ArTqbbaOVN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9dWnk6ArTqbbaOVN .rough-node .label text,#mermaid-svg-9dWnk6ArTqbbaOVN .node .label text,#mermaid-svg-9dWnk6ArTqbbaOVN .image-shape .label,#mermaid-svg-9dWnk6ArTqbbaOVN .icon-shape .label{text-anchor:middle;}#mermaid-svg-9dWnk6ArTqbbaOVN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9dWnk6ArTqbbaOVN .rough-node .label,#mermaid-svg-9dWnk6ArTqbbaOVN .node .label,#mermaid-svg-9dWnk6ArTqbbaOVN .image-shape .label,#mermaid-svg-9dWnk6ArTqbbaOVN .icon-shape .label{text-align:center;}#mermaid-svg-9dWnk6ArTqbbaOVN .node.clickable{cursor:pointer;}#mermaid-svg-9dWnk6ArTqbbaOVN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9dWnk6ArTqbbaOVN .arrowheadPath{fill:#333333;}#mermaid-svg-9dWnk6ArTqbbaOVN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9dWnk6ArTqbbaOVN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9dWnk6ArTqbbaOVN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9dWnk6ArTqbbaOVN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9dWnk6ArTqbbaOVN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9dWnk6ArTqbbaOVN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster text{fill:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN .cluster span{color:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9dWnk6ArTqbbaOVN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9dWnk6ArTqbbaOVN rect.text{fill:none;stroke-width:0;}#mermaid-svg-9dWnk6ArTqbbaOVN .icon-shape,#mermaid-svg-9dWnk6ArTqbbaOVN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9dWnk6ArTqbbaOVN .icon-shape p,#mermaid-svg-9dWnk6ArTqbbaOVN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9dWnk6ArTqbbaOVN .icon-shape .label rect,#mermaid-svg-9dWnk6ArTqbbaOVN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9dWnk6ArTqbbaOVN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9dWnk6ArTqbbaOVN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9dWnk6ArTqbbaOVN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

访问 obj.method()
获取 obj 的类型 cls
读取 cls.mro
从当前类开始查找 method
当前类是否定义 method?
返回并绑定方法
进入 MRO 中下一个类
执行方法

对于:

python 复制代码
class D(B, C):
    pass

查找路线是:

text 复制代码
D -> B -> C -> A -> object

记住这句话:

text 复制代码
MRO 决定查找顺序,super() 沿着 MRO 向后调用。

十七、总结:MRO 是 Python 多继承的秩序感

Python 之所以优雅,不只是因为语法简洁,还因为它在复杂问题上有清晰的规则。

MRO 解决的正是多继承中的核心问题:

text 复制代码
当多个父类都提供同名方法或属性时,Python 应该先找谁?

它通过 C3 线性化算法保证:

text 复制代码
子类优先于父类
父类声明顺序被尊重
共同祖先只出现一次
冲突继承会被明确拒绝

在日常开发中,你需要牢记:

text 复制代码
1. 使用 ClassName.__mro__ 或 ClassName.mro() 查看查找顺序
2. 多继承中,父类顺序会影响方法调用
3. super() 调用的是 MRO 中的下一个类,不一定是直接父类
4. Mixin 适合封装横向能力
5. 协作式多继承中,每一层都应该调用 super()
6. 复杂业务优先考虑组合,而不是滥用继承

MRO 看似是一个底层机制,但它影响着 Python 面向对象编程的方方面面。理解它以后,你会更容易读懂 Django、Flask、FastAPI、SQLAlchemy 等框架中的类设计,也能在自己的项目里写出更清晰、更稳定、更可维护的代码。

技术成长往往不是记住更多 API,而是理解那些看似隐藏、却支撑整个体系运转的底层规则。MRO 就是这样一个值得你真正掌握的 Python 核心概念。


互动问题

你在项目中遇到过多继承导致的方法调用顺序问题吗?

你更喜欢使用 Mixin 组合能力,还是倾向于使用对象组合来降低继承复杂度?

欢迎在评论区分享你的经验。一个真实的踩坑案例,往往比十条抽象规则更有价值。

相关推荐
python-码博士19 分钟前
PyTorch 从零实现 Flow Matching:训练、采样、画图一条龙
人工智能·pytorch·python
skywalk816327 分钟前
言知项目后续方向建议
开发语言·学习·编程
王小王-1231 小时前
基于Python的车联网数据聚合与可视化分析平台设计与实现
python·车联网·新能源汽车·车联网聚合分析
拉勾科研工作室1 小时前
区块链工程毕业论文题目【249个】
开发语言·javascript
叫我:松哥1 小时前
基于Flask框架的校园二手书籍交易平台,注重校园场景的特殊需求,通过学号认证保障用户真实性
后端·python·sqlite·flask·bootstrap
namexingyun1 小时前
开源前端生态如何成为 AI UI 生成的“燃料“:shadcn/ui、Tailwind CSS、Storybook 技术价值全解剖
java·前端·人工智能·python·ui·开源·ai编程
通信仿真爱好者1 小时前
第【17】期--考虑硬件损伤和不完美CSI的RIS-MISO系统的深度强化学习联合优化-python完整代码+参考文献
python·深度强化学习·ris
装不满的克莱因瓶2 小时前
自然语言处理常见任务——从文本理解到生成式AI的完整任务体系
人工智能·pytorch·python·深度学习·ai·自然语言处理
z落落2 小时前
C#WinForm控件实战:Panel与单选框动态创建
开发语言·c#
ptc学习者2 小时前
python 中描述符@property property 大概的样子
开发语言·python