
python是一种面向对象的编程语言,其继承机制允许一个类(子类)从另一个或多个类(父类)继承属性和方法。在python 3中,所有类都默认隐式地继承自内置的object类。即使我们定义一个简单的类class myclass:,它也等同于class myclass(object):。object类是所有类的基类,提供了诸如__init__、__str__、__eq__等基本方法和属性。
当一个类继承自多个父类时,就称为多重继承。Python通过方法解析顺序(Method Resolution Order, MRO)来确定在继承链中查找方法和属性的顺序。Python 3采用C3线性化算法来计算MRO,确保继承链的正确性和一致性。
考虑以下两种定义类Bar的方式,其中Foo是一个已经(隐式或显式)继承自object的类:
隐式继承object:
class Foo:
pass
class Bar(Foo):
pass在这种情况下,Bar直接继承自Foo。由于Foo隐式继承自object,所以Bar也间接继承自object。
立即学习“Python免费学习笔记(深入)”;
显式继承object:
class Foo:
pass
class Bar(Foo, object):
pass在这种情况下,Bar同时继承自Foo和object。
从直观上看,这两种定义方式似乎在功能上是等价的,因为object已经是所有类的最终基类。那么,显式地将object作为基类之一是否有实际意义呢?
MRO是Python处理多重继承的核心机制,它决定了当子类调用一个方法时,Python会按照什么顺序在父类中查找该方法。我们可以通过调用类的mro()方法或访问__mro__属性来查看MRO。
让我们通过示例代码来比较上述两种情况的MRO:
class Foo:
pass
# 情况1: 隐式继承object
class BarImplicit(Foo):
pass
print(f"BarImplicit MRO: {BarImplicit.mro()}")
# 输出: BarImplicit MRO: [<class '__main__.BarImplicit'>, <class '__main__.Foo'>, <class 'object'>]
# 情况2: 显式继承object
class BarExplicit(Foo, object):
pass
print(f"BarExplicit MRO: {BarExplicit.mro()}")
# 输出: BarExplicit MRO: [<class '__main__.BarExplicit'>, <class '__main__.Foo'>, <class 'object'>]从输出结果可以看出,BarImplicit和BarExplicit的MRO是完全相同的。这表明,在Python的C3线性化算法下,如果一个类已经通过其父类(如Foo)间接继承了object,那么在多重继承列表中再次显式地列出object并不会改变MRO的顺序。object总是MRO链中的最后一个类,因为它没有自己的父类。
除了MRO之外,我们还需要考虑这两种继承方式在实际功能行为上是否存在差异。
方法调用与属性访问: 由于MRO相同,因此当Bar的实例调用任何方法或访问任何属性时,其查找路径是完全一致的。这意味着在这方面,两种形式的功能是等价的。
isinstance()和issubclass()检查:isinstance()用于检查一个对象是否是某个类或其子类的实例,而issubclass()用于检查一个类是否是另一个类的子类。
class Foo:
pass
class BarImplicit(Foo):
pass
class BarExplicit(Foo, object):
pass
# 检查BarImplicit
print(f"isinstance(BarImplicit(), Foo): {isinstance(BarImplicit(), Foo)}") # True
print(f"isinstance(BarImplicit(), object): {isinstance(BarImplicit(), object)}") # True
print(f"issubclass(BarImplicit, Foo): {issubclass(BarImplicit, Foo)}") # True
print(f"issubclass(BarImplicit, object): {issubclass(BarImplicit, object)}") # True
# 检查BarExplicit
print(f"isinstance(BarExplicit(), Foo): {isinstance(BarExplicit(), Foo)}") # True
print(f"isinstance(BarExplicit(), object): {isinstance(BarExplicit(), object)}") # True
print(f"issubclass(BarExplicit, Foo): {issubclass(BarExplicit, Foo)}") # True
print(f"issubclass(BarExplicit, object): {issubclass(BarExplicit, object)}") # True上述示例表明,在isinstance()和issubclass()的检查中,两种形式也表现出完全相同的行为。
尽管MRO和功能行为相同,但有一个重要的内部属性会因显式继承object而改变,那就是__bases__。__bases__是一个元组,存储了类定义时直接指定的基类。
class Foo:
pass
class BarImplicit(Foo):
pass
class BarExplicit(Foo, object):
pass
print(f"BarImplicit.__bases__: {BarImplicit.__bases__}")
# 输出: BarImplicit.__bases__: (<class '__main__.Foo'>,)
print(f"BarExplicit.__bases__: {BarExplicit.__bases__}")
# 输出: BarExplicit.__bases__: (<class '__main__.Foo'>, <class 'object'>)从结果可以看出,BarImplicit.__bases__只包含Foo,而BarExplicit.__bases__则包含了Foo和object。
潜在影响:
然而,在绝大多数日常编程场景中,直接操作或依赖__bases__属性的情况非常罕见。通常,我们更关心的是MRO或isinstance()等行为。
综合来看,当一个类Foo已经继承自object时,在多重继承中显式地将object作为基类之一 (class Bar(Foo, object)) 几乎没有实际的功能性好处。
可能的历史原因或误解:
最佳实践:
除非有非常明确且充分的理由(例如,你正在编写一个需要严格检查__bases__的元类或框架),否则在Python 3中,当一个父类已经继承自object时,不应显式地将object列为基类。这样做只会增加代码的冗余,并可能引起不必要的困惑。保持代码简洁明了,遵循Python的惯例,即让object的继承保持隐式。
以上就是深入探讨Python多重继承中显式继承object的必要性与影响的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号