关注「索引目录」公众号,获取更多干货。
为什么 super() 不表示“父类”(以及它真正的作用)
蒂莫西茫然地盯着屏幕。他写的明明是简单的继承代码,但输出结果却完全不知所云。
class A:
def process(self):
print("A.process()")
class B(A):
def process(self):
print("B.process()")
super().process()
class C(A):
def process(self):
print("C.process()")
super().process()
class D(B, C):
def process(self):
print("D.process()")
super().process()
d = D()
d.process()
他预期输出结果为:
D.process()
B.process()
A.process()
毕竟,D继承自B,而又B继承自A。继承就是这样运作的,对吧?
但当他运行代码时,却得到了以下结果:
D.process()
B.process()
C.process()
A.process()
“什么?!”蒂莫西惊呼道,吸引了图书馆另一头的玛格丽特的注意。“为什么会C.process()调用?类B又不继承自C!当B调用时super().process(),它应该调用A.process(),而不是C.process()!”
玛格丽特走了过来,脸上带着一丝了然的微笑。“啊,你发现了Python中最容易被误解的特性之一。你以为super()`__init__`的意思是‘调用我的父类’,对吧?”
“当然会!super()它调用的是超类——父类!”
“没错,”玛格丽特温和地说,“大家都是这么想的。但这完全错了。super()蒂莫西,它不会调用你的父类,而是调用方法解析顺序中的下一个类。”
“什么……什么?”
“来,”玛格丽特说着,打开一本厚厚的书,书名是《方法解析顺序:Python的隐藏路径》。“让我来给你展示一下Python究竟是如何解析方法的。”
方法决议令(MRO)
“Python 中的每个类,”玛格丽特开始说道,“都有一个名为‘方法解析顺序’的属性__mro__。它是一个元组,定义了 Python 在查找属性时遵循的确切顺序。”
她输入:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.__mro__)
输出:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
<class '__main__.A'>, <class 'object'>)
玛格丽特解释说:“这个元组就是 Python 搜索方法和属性的顺序。当你访问某个对象时d.process(),Python 会查找:”
-
首先是 D -
然后 B -
然后 C -
然后 A -
最后在 object(所有类的根)
关键在于:super()它指的不是“我的父级”,而是“MRO中的下一级”。
“MRO(维护、维修和大修)也会影响其他操作,”玛格丽特补充道。“例如,isinstance()还要issubclass()进行MRO巡检:”
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
obj = D()
# isinstance() checks if the object's class is in the MRO
print(isinstance(obj, D)) # True - D is in D's MRO
print(isinstance(obj, B)) # True - B is in D's MRO
print(isinstance(obj, C)) # True - C is in D's MRO
print(isinstance(obj, A)) # True - A is in D's MRO
print(isinstance(obj, object)) # True - object is always in MRO
# issubclass() checks if one class appears after another in the MRO
print(issubclass(D, B)) # True
print(issubclass(D, C)) # True
print(issubclass(D, A)) # True
# The MRO makes these checks simple and efficient
print(D.__mro__)
# All the classes where isinstance returns True!
蒂莫西仔细研究了MRO。“所以当B.process()调用时super().process(),它不会查看B的父类A。它会查看MRO中B之后的下一个类,也就是…… C!”
“没错!让我们一步一步地分析你最初的例子:”
class A:
def process(self):
print("A.process()")
class B(A):
def process(self):
print("B.process()")
super().process() # Next in MRO after B
class C(A):
def process(self):
print("C.process()")
super().process() # Next in MRO after C
class D(B, C):
def process(self):
print("D.process()")
super().process() # Next in MRO after D
# The MRO is: D -> B -> C -> A -> object
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d = D()
d.process()
玛格丽特追踪了行刑过程:
1. d.process() starts in D
- Prints "D.process()"
- Calls super().process() → next in MRO is B
2. B.process() executes
- Prints "B.process()"
- Calls super().process() → next in MRO is C (not A!)
3. C.process() executes
- Prints "C.process()"
- Calls super().process() → next in MRO is A
4. A.process() executes
- Prints "A.process()"
- No super() call, done
玛格丽特强调说:“关键在于,它super()既不B知道也不关心它B继承自哪个实例。它只知道正在操作的实例A的 MRO——在本例中,就是一个实例。”D
蒂莫西瞪大了眼睛。“所以同一个方法B.process()会根据实例的类不同而调用不同的后续方法?”
“没错!看:”
# Same classes as before
# Instance of B directly
b = B()
print("MRO of B:", B.__mro__)
# (<class 'B'>, <class 'A'>, <class 'object'>)
b.process()
# B.process()
# A.process()
# Instance of D
d = D()
print("\nMRO of D:", D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d.process()
# D.process()
# B.process()
# C.process()
# A.process()
“当你调用b.process()一个B实例时,MRO 是B -> A -> object,所以super()在B调用中A。但是当你调用d.process()一个D实例时,MRO 是D -> B -> C -> A -> object,所以super()在B调用中C!”
Python 如何构建 MRO:C3 线性化
“但是Python是如何确定这个顺序的呢?”蒂莫西问道,“为什么是MROD -> B -> C -> A而不是其他顺序?”
玛格丽特拿出一张图表。“Python 使用一种名为C3 线性化(也称为 C3 超类线性化)的算法。它有三个主要规则:
- 儿童优先于父母
(本地优先顺序) - 从左到右排序
(来自继承列表) - 单调性
(保持与父级 MRO 相同的顺序)
单调性规则至关重要:如果 A 类出现在任何父类的 MRO 中,且出现在 B 类之前,那么 A 类必须出现在子类的 MRO 中,且出现在 B 类之前(除非在子类的直接继承列表中明确地覆盖)。
让我来演示一下它是如何为课堂构建MRO的D(B, C):
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
玛格丽特写出了算法:
Step 1: Start with the class itself
L(D) = D + merge(L(B), L(C), [B, C])
Step 2: Expand the parent linearizations
L(B) = B, A, object
L(C) = C, A, object
L(D) = D + merge([B, A, object], [C, A, object], [B, C])
Step 3: Apply merge algorithm
- Take the first head that doesn't appear in any other tail
- "Head" = first element of a list
- "Tail" = everything after the first element
merge([B, A, object], [C, A, object], [B, C]):
First heads: B, C, B
B is not in any tail → take B
D, B + merge([A, object], [C, A, object], [C]):
First heads: A, C, C
A is in tail of [C, A, object] → skip
C is not in any tail → take C
D, B, C + merge([A, object], [A, object]):
First heads: A, A
A is not in any tail → take A
D, B, C, A + merge([object], [object]):
First head: object
object is not in any tail → take object
Final: D, B, C, A, object
“该算法确保了几个重要的特性,”玛格丽特解释说:
# Property 1: A class always comes before its parents
class Parent:
pass
class Child(Parent):
pass
print(Child.__mro__)
# (<class 'Child'>, <class 'Parent'>, <class 'object'>)
# Child before Parent ✓
# Property 2: Parents are in the order specified
class A:
pass
class B:
pass
class C(A, B): # A before B
pass
print(C.__mro__)
# (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)
# A appears before B ✓
# Property 3: A class's MRO is consistent with its parents' MROs
class D(B, A): # B before A
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'A'>, <class 'object'>)
“但是,”蒂莫西问道,“如果你创建的继承层次结构违反了这些规则,会发生什么呢?”
玛格丽特笑了。“问得好。Python 拒绝创建这样的类。”
class X:
pass
class Y(X):
pass
class Z(X, Y): # Try to put X before Y, but Y inherits from X
pass
# TypeError: Cannot create a consistent method resolution
# order (MRO) for bases X, Y
“算法声明中说‘X 必须在 Y 之前’ Z(X, Y),但同时又说‘Y 必须在 X 之前’(因为 YY继承自 X X)。这是不可能的,所以 Python 会抛出错误。”
“这里又出现了一个违反单调性的例子:”
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
# D's MRO: D -> B -> C -> A
# This respects that B.MRO has (B, A) and C.MRO has (C, A)
# But what if we try:
class E(C, B):
pass
# E's MRO: E -> C -> B -> A
# This also works! Order reversed but still consistent
# However, this breaks:
class F(A, B): # Try to put A before B, but B inherits from A
pass
# TypeError: Cannot create a consistent method resolution
# order (MRO) for bases A, B
#
# We're saying "A before B" but B's MRO says "B before A"
# Inconsistent!
“C3 算法可以检测出这些不一致之处,并防止您创建不可能的层级结构。”
钻石问题
“现在让我们来看看为什么MRO如此重要,”玛格丽特一边说着,一边在石板上画了一张图:
A
/ \
B C
\ /
D
“这就是所谓的菱形问题。类 AD继承自类 BB和C类 C,而类 B 又继承自类AC。问题是:当D类 A 调用所有类都实现的方法时,这些方法的调用顺序是什么?”
class A:
def __init__(self):
print("A.__init__")
class B(A):
def __init__(self):
print("B.__init__")
super().__init__()
class C(A):
def __init__(self):
print("C.__init__")
super().__init__()
class D(B, C):
def __init__(self):
print("D.__init__")
super().__init__()
d = D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
“注意一个关键点,”玛格丽特指出,“尽管两者B都C继承自A,A.__init__但只调用一次。这是有意为之。最小可行优化(MRO)确保每个类都恰好出现一次,并super()确保我们正确地遵循了MRO。”
“如果我们不用呢super()?”蒂莫西问道。
class A:
def __init__(self):
print("A.__init__")
class B(A):
def __init__(self):
print("B.__init__")
A.__init__(self) # Direct call instead of super()
class C(A):
def __init__(self):
print("C.__init__")
A.__init__(self) # Direct call instead of super()
class D(B, C):
def __init__(self):
print("D.__init__")
B.__init__(self)
C.__init__(self)
d = D()
# D.__init__
# B.__init__
# A.__init__
# C.__init__
# A.__init__ ← Called twice!
“糟了!”玛格丽特惊呼道。“A.__init__它被调用了两次。在这个例子中,这似乎没什么大不了的,但想象一下,如果A它是用来打开文件、创建数据库连接或分配资源,那就会造成资源重复,甚至可能导致数据损坏。”
“MRO plussuper()通过确保每个类都以可预测的顺序被调用一次来解决这个问题。”
合作多重继承
玛格丽特解释说: “MRO 的真正威力super()在于设计能够协同工作的类。这需要一种特定的模式。”
她举了一个例子:
class LoggingMixin:
def __init__(self, **kwargs):
print(f"LoggingMixin.__init__")
super().__init__(**kwargs) # Pass remaining kwargs along
class ValidationMixin:
def __init__(self, **kwargs):
print(f"ValidationMixin.__init__")
super().__init__(**kwargs)
class Base:
def __init__(self, name):
print(f"Base.__init__: name={name}")
self.name = name
class Document(LoggingMixin, ValidationMixin, Base):
def __init__(self, name, **kwargs):
print(f"Document.__init__")
super().__init__(name=name, **kwargs)
doc = Document("report.pdf", extra="value")
# Document.__init__
# LoggingMixin.__init__
# ValidationMixin.__init__
# Base.__init__: name=report.pdf
print(Document.__mro__)
# (<class 'Document'>, <class 'LoggingMixin'>,
# <class 'ValidationMixin'>, <class 'Base'>, <class 'object'>)
“请注意这里几个关键的模式,”玛格丽特指出:
模式 1:接受并传递 **kwargs
“除了最终的基类之外,每个类都接受**kwargs并传递这些参数super()。这使得参数能够流经整个 MRO 链。”
模式二:仅提取所需内容
class TimestampMixin:
def __init__(self, timestamp=None, **kwargs):
self.timestamp = timestamp or datetime.now()
super().__init__(**kwargs) # Pass the rest along
class AuthorMixin:
def __init__(self, author=None, **kwargs):
self.author = author or "Unknown"
super().__init__(**kwargs)
class Base:
def __init__(self, name):
self.name = name
class Article(TimestampMixin, AuthorMixin, Base):
def __init__(self, name, **kwargs):
super().__init__(name=name, **kwargs)
article = Article(
name="Python Deep Dive",
author="Margaret",
timestamp=datetime(2024, 1, 1)
)
print(f"{article.name} by {article.author} at {article.timestamp}")
每个 mixin 都会从其他 mixin 中提取参数**kwargs,并将剩余的参数传递给下一个 mixin。这个过程会一直持续,直到所有参数都被消耗掉为止。
模式 3:始终调用 super()
“链中的每个类都必须调用super().__init__(),即使它认为自己没有父类。记住,遵循实例super()的最小可复现关系(MRO),而不是你在代码中看到的类层次结构。”
class Mixin:
def __init__(self, **kwargs):
print("Mixin.__init__")
# Even though Mixin doesn't explicitly inherit from anything,
# we still call super() to continue the MRO chain
super().__init__(**kwargs)
class Base:
def __init__(self, value):
print(f"Base.__init__: value={value}")
self.value = value
class Combined(Mixin, Base):
def __init__(self, value, **kwargs):
super().__init__(value=value, **kwargs)
obj = Combined(value=42)
# Mixin.__init__
# Base.__init__: value=42
蒂莫西皱起了眉头。“但是如果我把使用某种技术的课程super()和不使用这种技术的课程混在一起呢?”
“一片混乱,”玛格丽特冷冷地说。
class A:
def __init__(self):
print("A.__init__")
class B(A):
def __init__(self):
print("B.__init__")
A.__init__(self) # Direct call - breaks the chain!
class C(A):
def __init__(self):
print("C.__init__")
super().__init__() # Uses super()
class D(B, C):
def __init__(self):
print("D.__init__")
super().__init__()
d = D()
# D.__init__
# B.__init__
# A.__init__ ← Chain stops here! C is never called
“因为直接B调用A而不是使用super(),导致 MRO 链被破坏。C.__init__即使它位于 MRO 中,也从未被调用。规则是:要么层次结构中的所有类都使用super(),要么都不使用。 ”
super() 不带参数(Python 3 的神奇之处)
“你可能已经注意到了,”玛格丽特说,“我们一直super()没有使用任何参数。在 Python 2 中,你必须这样写super(CurrentClass, self)。Python 3 如何知道要使用哪个类和实例呢?”
蒂莫西想了想。“某种……魔法?”
“闭包,”玛格丽特笑着说。“这是对 Python 闭包机制的巧妙运用。当一个类被创建时,Python 会将一个名为 `close` 的特殊闭包变量绑定__class__到任何使用该闭包的方法上super()。”
class Example:
def method(self):
print(super())
# Python transforms this to:
# print(super(__class__, self))
# where __class__ is bound at class creation time
# Let's peek at the closure
import inspect
print(Example.method.__code__.co_freevars)
# ('__class__',)
# The method has a closure that captures the class
print(Example.method.__closure__)
# (<cell at 0x...: type object at 0x...>,)
当你super()不带任何参数调用 Python 时:
__class__在函数闭包中查找(在类创建时绑定) -
从局部作用域获取 self(或对于类方法)。cls -
有效地呼叫 super(__class__, self)
引用__class__是在类创建时绑定的,而不是在调用时绑定的,这就是为什么即使类被重命名也能正常工作的原因:
class Original:
def method(self):
return super()
# Rename the class
Renamed = Original
obj = Renamed()
print(obj.method())
# <super: <class 'Original'>, <Renamed object>>
# Still knows the original class name!
# Even if we delete the original name binding
Temp = Original
del Original
obj = Temp()
obj.method() # Still works! __class__ was bound at class creation
但有些情况下,你仍然需要明确的论证:”
class A:
def method(self):
return "A.method"
class B(A):
def method(self):
return "B.method"
class C(B):
def method(self):
return "C.method"
def skip_parent(self):
"""Skip B and go directly to A in the MRO"""
# Normal super() would call B.method
result_b = super().method() # Calls B.method
# But we can explicitly skip B by using super(B, self)
# This starts the search AFTER B in the MRO
result_a = super(B, self).method() # Calls A.method
return f"Normal: {result_b}, Skipped: {result_a}"
obj = C()
print(obj.skip_parent())
# Normal: B.method, Skipped: A.method
# Another case: calling super() outside a method
class D(A):
# This doesn't work - no __class__ closure
# external_super = super() # RuntimeError: super(): no arguments
def method(self):
# But this works - inside a method with __class__ closure
return super()
# Also useful: super() in a function that's assigned to a class later
def external_method(self):
# No __class__ closure here
# return super().method() # RuntimeError
# Must use explicit form
return super(D, self).method()
D.external = external_method
现实世界模式:混合
“让我们看看真正的框架是如何使用多重继承和 MRO 的,”玛格丽特说着,打开了一个 Django 模型参考文档。
from datetime import datetime
# Mixin classes provide discrete functionality
class TimestampMixin:
"""Adds created_at and updated_at fields"""
def __init__(self, **kwargs):
self.created_at = datetime.now()
self.updated_at = datetime.now()
super().__init__(**kwargs)
def touch(self):
"""Update the updated_at timestamp"""
self.updated_at = datetime.now()
class SoftDeleteMixin:
"""Adds soft delete functionality"""
def __init__(self, **kwargs):
self.is_deleted = False
self.deleted_at = None
super().__init__(**kwargs)
def delete(self):
"""Soft delete - mark as deleted instead of removing"""
self.is_deleted = True
self.deleted_at = datetime.now()
def restore(self):
"""Restore a soft-deleted record"""
self.is_deleted = False
self.deleted_at = None
class AuditMixin:
"""Adds audit trail functionality"""
def __init__(self, **kwargs):
self.created_by = kwargs.pop('created_by', None)
self.updated_by = kwargs.pop('updated_by', None)
super().__init__(**kwargs)
def update_audit(self, user):
"""Update audit information"""
self.updated_by = user
if hasattr(self, 'touch'):
self.touch()
# Base model class
class Model:
"""Base class for all models"""
def __init__(self, id=None):
self.id = id
def save(self):
print(f"Saving {self.__class__.__name__} with id={self.id}")
# Combine mixins to create feature-rich models
class BlogPost(AuditMixin, TimestampMixin, SoftDeleteMixin, Model):
"""A blog post with full audit trail, timestamps, and soft delete"""
def __init__(self, id, title, content, **kwargs):
self.title = title
self.content = content
super().__init__(id=id, **kwargs)
# Create a blog post
post = BlogPost(
id=1,
title="Understanding super()",
content="It's all about the MRO!",
created_by="Margaret"
)
print(f"Post: {post.title}")
print(f"Created: {post.created_at}")
print(f"By: {post.created_by}")
print(f"Deleted: {post.is_deleted}")
# Use mixin functionality
post.delete()
print(f"After delete - Deleted: {post.is_deleted}")
post.restore()
print(f"After restore - Deleted: {post.is_deleted}")
# Check the MRO
print("\nMRO:")
for cls in BlogPost.__mro__:
print(f" {cls.__name__}")
# BlogPost
# AuditMixin
# TimestampMixin
# SoftDeleteMixin
# Model
# object
“这种模式在 Django 中随处可见,”玛格丽特解释说。“Mixin 让你无需深层的继承层次结构就能组合功能。每个 mixin 都专注于一个方面,你可以根据需要将它们组合起来。”
常见陷阱和调试
“让我来向你们展示最常见的错误,”玛格丽特说着,打开了题为“当MRO出错时”的章节。
陷阱一:方法签名不一致
class A:
def process(self, data):
print(f"A: {data}")
class B(A):
def process(self, data, extra): # Different signature!
print(f"B: {data}, {extra}")
super().process(data)
class C(A):
def process(self, data):
print(f"C: {data}")
super().process(data)
class D(B, C):
def process(self, data, extra):
super().process(data, extra)
# d = D()
# d.process("test", "extra") # TypeError!
# C.process() doesn't accept 'extra' parameter
解决方案:**kwargs用于处理不同的签名:
class A:
def process(self, data, **kwargs):
print(f"A: {data}")
class B(A):
def process(self, data, extra=None, **kwargs):
print(f"B: {data}, {extra}")
super().process(data, **kwargs)
class C(A):
def process(self, data, **kwargs):
print(f"C: {data}")
super().process(data, **kwargs)
class D(B, C):
def process(self, data, extra=None, **kwargs):
super().process(data, extra=extra, **kwargs)
d = D()
d.process("test", extra="value")
# B: test, value
# C: test
# A: test
陷阱 2:在某些方法中调用 super() 方法
class Mixin1:
def __init__(self, **kwargs):
super().__init__(**kwargs) # Good - calls super()
self.mixin1_setup = True
class Mixin2:
def __init__(self, **kwargs):
# Bad - no super() call!
self.mixin2_setup = True
class Base:
def __init__(self):
self.base_setup = True
class Combined(Mixin1, Mixin2, Base):
def __init__(self):
super().__init__()
obj = Combined()
print(hasattr(obj, 'mixin1_setup')) # True
print(hasattr(obj, 'mixin2_setup')) # True
print(hasattr(obj, 'base_setup')) # False - Base.__init__ never called!
陷阱 3:假设父类关系
class Payment:
def process(self):
print("Processing payment...")
return True
class CreditCardPayment(Payment):
def process(self):
print("Validating credit card...")
# Assumption: super() will call Payment.process()
return super().process()
class SecurePayment:
def process(self):
print("Applying security checks...")
return True # Oops, doesn't call super()!
class SecureCreditCard(CreditCardPayment, SecurePayment):
pass
# What's the MRO?
print(SecureCreditCard.__mro__)
# SecureCreditCard -> CreditCardPayment -> SecurePayment -> Payment -> object
payment = SecureCreditCard()
result = payment.process()
# Validating credit card...
# Applying security checks...
# (Payment.process() is never called!)
“问题在于它SecurePayment.process()不打电话super()。链条到此就断了。”
调试工具:MRO可视化
def print_mro(cls, indent=0):
"""Print the MRO as a tree"""
print(" " * indent + cls.__name__)
def trace_super_calls(cls):
"""Show the complete super() chain for a class"""
print(f"super() chain for {cls.__name__}:")
for i, klass in enumerate(cls.__mro__):
print(f" {i}. {klass.__name__}")
if hasattr(klass, '__init__'):
# Check if __init__ calls super()
import inspect
source = inspect.getsource(klass.__init__)
has_super = 'super()' in source
print(f" __init__ calls super(): {has_super}")
# Example usage
class A:
def __init__(self):
super().__init__()
class B(A):
def __init__(self):
super().__init__()
class C(A):
def __init__(self):
super().__init__()
class D(B, C):
def __init__(self):
super().__init__()
trace_super_calls(D)
何时不应使用多重继承
“多重继承很强大,”玛格丽特告诫说,“但它并不总是正确的工具。”
在以下情况下使用多重继承:
-
你需要组合正交行为(mixin)。 -
这些混合元素没有重叠的属性。 -
所有班级都与……合作 super() -
MRO 符合直觉。
避免多重继承的情况:
-
类中存在相互冲突的方法实现 -
你继承自多个具体类(而非 mixin)。 -
继承层次结构不明确 -
构图会更简单
# Instead of this:
class FileLogger:
def log(self, msg):
with open('log.txt', 'a') as f:
f.write(msg)
class ConsoleLogger:
def log(self, msg):
print(msg)
class DualLogger(FileLogger, ConsoleLogger): # Conflict!
pass
# Consider this:
class Logger:
def __init__(self, *loggers):
self.loggers = loggers
def log(self, msg):
for logger in self.loggers:
logger.log(msg)
file_logger = FileLogger()
console_logger = ConsoleLogger()
dual_logger = Logger(file_logger, console_logger)
super()“即使有MRO ,组合通常也比继承更好。”
高级模式:带有 MRO 的抽象基类
“再举一个更高级的例子,”玛格丽特说着,拿出了最后一个例子。“抽象基类结合混入(mixin)可以创建强大且类型安全的架构:”
from abc import ABC, abstractmethod
class StorageBackend(ABC):
"""Abstract base class defining storage interface"""
@abstractmethod
def save(self, key, value):
pass
@abstractmethod
def load(self, key):
pass
# Cannot instantiate abstract class
try:
storage = StorageBackend()
except TypeError as e:
print(f"Error: {e}")
# Error: Can't instantiate abstract class StorageBackend with abstract methods load, save
class CompressionMixin:
"""Mixin that adds compression to storage"""
def save(self, key, value, **kwargs):
compressed = self._compress(value)
return super().save(key, compressed, **kwargs)
def load(self, key, **kwargs):
compressed = super().load(key, **kwargs)
return self._decompress(compressed)
def _compress(self, data):
return f"compressed({data})"
def _decompress(self, data):
return data.replace("compressed(", "").rstrip(")")
class EncryptionMixin:
"""Mixin that adds encryption to storage"""
def save(self, key, value, **kwargs):
encrypted = self._encrypt(value)
return super().save(key, encrypted, **kwargs)
def load(self, key, **kwargs):
encrypted = super().load(key, **kwargs)
return self._decrypt(encrypted)
def _encrypt(self, data):
return f"encrypted({data})"
def _decrypt(self, data):
return data.replace("encrypted(", "").rstrip(")")
class DictStorage(StorageBackend):
"""Concrete storage implementation using dict"""
def __init__(self):
self.storage = {}
def save(self, key, value):
print(f"DictStorage.save: {key} = {value}")
self.storage[key] = value
def load(self, key):
value = self.storage[key]
print(f"DictStorage.load: {key} = {value}")
return value
# Now DictStorage can be instantiated - it implements all abstract methods
storage = DictStorage() # Works!
# Compose features with mixins
class SecureStorage(EncryptionMixin, CompressionMixin, DictStorage):
"""Storage with both encryption and compression"""
pass
# Check the MRO
print("MRO:", [cls.__name__ for cls in SecureStorage.__mro__])
# ['SecureStorage', 'EncryptionMixin', 'CompressionMixin', 'DictStorage',
# 'StorageBackend', 'ABC', 'object']
# Use it
storage = SecureStorage()
storage.save("key1", "secret data")
# DictStorage.save: key1 = encrypted(compressed(secret data))
loaded = storage.load("key1")
# DictStorage.load: key1 = encrypted(compressed(secret data))
print(f"Loaded: {loaded}")
# Loaded: secret data
“请注意数据在MRO中的流动方式:
保存:
EncryptionMixin.save()加密 → 调用 super()CompressionMixin.save()压缩 → 调用 super()DictStorage.save()存储加密压缩数据
加载中:
DictStorage.load()检索加密压缩数据 CompressionMixin.load()解压缩 → 返回给调用者 EncryptionMixin.load()解密 → 返回调用者
每个 mixin 都会在两个方向上添加自己的行为。这就是协作式多重继承的威力!
结论:MRO 精通
蒂莫西合上笔记本,之前的困惑被理解所取代。“所以,super()它指的不是‘我的父类’,而是‘MRO中的下一个类’。而MRO是通过C3线性化计算出来的,以确保顺序的一致性和可预测性。”
“正是如此,”玛格丽特点点头。“需要记住的关键原则:
-
每个类别都有一个由C3线性化确定的MRO。 super()遵循实例的最小可复现规则 (MRO),而不是类层次结构。 - (在合作层级结构中)始终要调用
super()以延续链式调用 - 用于
**kwargs通过链式传递参数 - Mixins 应该是正交的
,并且专注于单一问题。 cls.__mro__调试时请检查 MRO 。 isinstance()还要issubclass()走一走MRO
MRO 使 Python 的多重继承能够可靠地工作。没有它,菱形继承将会一片混乱。有了它,你可以用简单的 mixin 构建复杂的行为。
“最后一点,”玛格丽特补充道,“super()它还会以有趣的方式与描述符和属性进行交互。当你super()在属性的 getter 中调用它时,需要注意递归问题。但这属于高级主题,它建立在描述符协议和 MRO 的基础之上。”
蒂莫西用全新的视角审视了他最初那个令人困惑的例子:
class D(B, C):
def process(self):
super().process() # Not "parent" - it's "next in D's MRO"
# When called on a D instance, the MRO is:
# D -> B -> C -> A -> object
# So super() in each class calls the NEXT one in THIS order
“现在一切都说得通了。这种行为并不神秘——它遵循着清晰、可预测的算法。”
“没错,”玛格丽特笑着说。“现在你明白了Python最复杂的功能之一。说到复杂的功能,”她眼中闪过一丝光芒,补充道,“你有没有想过类本身是如何创建的?class语法又是如何运作的?”
蒂莫西瞪大了眼睛。“你是说……元类吗?”
“的确如此,蒂莫西。不过,”玛格丽特说着,站起身,把那本书放回书架,“那是另一个故事了。以后有机会再深入探讨Python的对象模型。”
“经历了多次继承和MRO(抵押贷款审查官)之后,”蒂莫西笑着说,“我想我已经准备好应对任何事情了。”
玛格丽特笑了。“我们走着瞧,蒂莫西。我们走着瞧……”
要点总结
super()调用 MRO 中的下一个类,而不是父类。 - MRO 是使用 C3 线性化方法,
并采用一致、可预测的规则计算得出的。 -
在军事审查办公室(MRO)中,每个职业都只出现一次。 - 协作式多重继承需要
super()在每个方法中调用。 - 用于
**kwargs通过继承链传递参数 - Mixins 能够组合
正交行为 - Python 3 会
super()自动确定类和实例 -
使用 cls.__mro__以下方法调试 MRO 问题:cls.mro() - 避免
在组合关系清晰的情况下进行多重继承。 - 合作层次结构中的所有类都
必须一致地使用 super()
《Python的秘密生活》下一篇:“元类:创建类的类”
讨论问题
-
多重继承何时适用,而组合继承何时适用? -
流行的框架(Django、Flask)是如何使用 mixin 的? -
深度MRO链对性能有何影响? -
它如何 super()处理属性和描述符? -
能否创建一份违反规则的维护、维修和评估报告(MRO)?会发生什么?
关注「索引目录」公众号,获取更多干货。

