Python class Dog: 与 class Dog(): 的区别——彻底搞懂类的继承与 object 基类
本文档面向零基础新手,目标是让你真正理解:
class Dog:和class Dog():到底有没有区别- Python 2 和 Python 3 的历史背景
- 什么是
object,为什么它是所有类的基类 - 显式继承 vs 隐式继承的写法差异
- 单继承、多继承的写法
isinstance、issubclass、type()的行为- 继承链(MRO)是什么
super()的正确用法- 实际开发中应该怎么写
配有大量可运行示例,全部从最基础讲起。
第一部分:直接回答——有没有区别?
1.1 结论先行
# 以下三种写法,在 Python 3 中完全等价,没有任何区别
class Dog: # 写法1:现代简洁写法(Python 3 推荐)
pass
class Dog(): # 写法2:空括号(Python 3 中和写法1完全相同)
pass
class Dog(object): # 写法3:显式继承 object(Python 2/3 通用写法)
pass
结论:
在 Python 3 中,class Dog: 和 class Dog(): 和 class Dog(object):
三者完全等价,生成的类没有任何区别。
推荐写法:class Dog:(最简洁,Python 3 标准风格)
1.2 用代码验证
class Dog:
pass
class Cat():
pass
class Bird(object):
pass
# 查看每个类的基类
print(Dog.__bases__) # (<class 'object'>,)
print(Cat.__bases__) # (<class 'object'>,)
print(Bird.__bases__) # (<class 'object'>,)
# 三个类的基类完全相同,都是 object
print(Dog.__bases__ == Cat.__bases__ == Bird.__bases__) # True
# 查看 MRO(方法解析顺序)
print(Dog.__mro__) # (<class '__main__.Dog'>, <class 'object'>)
print(Cat.__mro__) # (<class '__main__.Cat'>, <class 'object'>)
print(Bird.__mro__) # (<class '__main__.Bird'>, <class 'object'>)
# isinstance 行为完全一致
d = Dog()
print(isinstance(d, Dog)) # True
print(isinstance(d, object)) # True(所有对象都是 object 的实例!)
# issubclass 行为完全一致
print(issubclass(Dog, object)) # True
print(issubclass(Cat, object)) # True
print(issubclass(Bird, object)) # True
第二部分:为什么会有这两种写法——历史背景
2.1 Python 2 时代的”新式类”与”旧式类”
Python 2(2.x版本)时期,存在两种类:
旧式类(经典类,classic class):
class Dog: ← Python 2 中这样写是旧式类
class Dog(): ← Python 2 中这样写也是旧式类
新式类(new-style class):
class Dog(object): ← Python 2 中必须显式继承 object 才是新式类
旧式类的问题(Python 2 特有):
① type(instance) 和 instance.__class__ 返回结果不同
② 多继承时,方法解析顺序(MRO)混乱
③ super() 不能正常工作
④ property、classmethod、staticmethod 等描述符不能正常工作
⑤ __slots__ 不能正常工作
新式类解决了所有这些问题。
所以 Python 2 程序员养成了写 class Dog(object): 的习惯。
2.2 Python 3 统一了一切
Python 3(3.x版本):
① 彻底废弃了旧式类
② 所有类,无论怎么写,都自动继承 object
③ class Dog: 和 class Dog(): 和 class Dog(object): 完全相同
所以在 Python 3 中:
class Dog: ← 新式类(自动继承 object)
class Dog(): ← 新式类(自动继承 object)
class Dog(object): ← 新式类(显式继承 object)
三者没有区别。
2.3 为什么现在还会看到 class Dog(object):这种写法?
① 历史遗留代码:从 Python 2 迁移过来的老代码
② 兼容性意图:作者想让代码同时兼容 Python 2 和 Python 3
③ 个人习惯:有些程序员从 Python 2 时代养成的习惯沿用至今
④ 明确性:一些团队规范要求显式写出继承,让代码"自文档化"
如果你看到别人写 class Dog(object):,不要疑惑:
他只是在告诉你"这个类继承自 object",和 class Dog: 完全相同。
第三部分:object 是什么?
3.1 object 是所有类的祖先
# Python 3 中,object 是一切类的基类(根基类)
print(object) # <class 'object'>
print(type(object)) # <class 'type'>
# 内置类型都继承自 object
print(int.__bases__) # (<class 'object'>,)
print(str.__bases__) # (<class 'object'>,)
print(list.__bases__) # (<class 'object'>,)
print(dict.__bases__) # (<class 'object'>,)
print(bool.__bases__) # (<class 'int'>,) ← bool 继承自 int,int 继承自 object
# 自定义类也继承自 object
class MyClass:
pass
print(MyClass.__bases__) # (<class 'object'>,)
# 任何对象都是 object 的实例
print(isinstance(42, object)) # True
print(isinstance("hello", object)) # True
print(isinstance([1, 2, 3], object)) # True
print(isinstance(MyClass(), object)) # True
print(isinstance(None, object)) # True!None 也是 object 的实例
print(isinstance(lambda: 0, object)) # True!函数也是 object 的实例
3.2 object 提供了哪些默认方法?
# object 提供了所有类都会继承的基础方法
class Dog:
pass # 什么都没定义
d = Dog()
# ===== 从 object 继承来的方法 =====
# __str__:转为字符串(print 时调用)
print(str(d)) # <__main__.Dog object at 0x...>(内存地址)
# __repr__:官方字符串表示
print(repr(d)) # <__main__.Dog object at 0x...>
# __class__:所属的类
print(d.__class__) # <class '__main__.Dog'>
# __dir__:列出所有属性和方法
print(dir(d)) # 包含很多从 object 继承的魔术方法
# __hash__:哈希值(用于字典键、集合成员)
print(hash(d)) # 某个整数(基于内存地址)
# __eq__:相等比较(默认比较是否为同一个对象)
d1 = Dog()
d2 = Dog()
print(d1 == d2) # False(不同对象,即使内容相同)
print(d1 == d1) # True(同一个对象)
# __ne__:不等比较
print(d1 != d2) # True
# __sizeof__:对象占用内存大小(字节)
print(d.__sizeof__())
# 查看 object 自带的所有属性
print([attr for attr in dir(object) if not attr.startswith('__')])
# [] ← object 自身没有任何普通属性,只有魔术方法
print([attr for attr in dir(object) if attr.startswith('__')])
# ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__',
# '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
# '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__',
# '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
# '__sizeof__', '__str__', '__subclasshook__']
3.3 覆盖 object 的默认方法
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
# 覆盖 object 的 __str__(print 时使用)
def __str__(self):
return f"🐕 {self.name}({self.age}岁)"
# 覆盖 object 的 __repr__(调试时使用)
def __repr__(self):
return f"Dog(name={self.name!r}, age={self.age})"
# 覆盖 object 的 __eq__(自定义相等条件)
def __eq__(self, other):
if not isinstance(other, Dog):
return NotImplemented
return self.name == other.name and self.age == other.age
# 覆盖 __hash__(自定义 __eq__ 后必须同时定义 __hash__)
def __hash__(self):
return hash((self.name, self.age))
d1 = Dog("旺财", 3)
d2 = Dog("旺财", 3)
d3 = Dog("小白", 5)
# 现在 __str__ 是我们自定义的
print(d1) # 🐕 旺财(3岁)
print(repr(d1)) # Dog(name='旺财', age=3)
# 现在 __eq__ 是我们自定义的(按内容比较,不再按内存地址)
print(d1 == d2) # True(内容相同)
print(d1 == d3) # False(内容不同)
# 可以放入集合(因为有 __hash__)
dogs = {d1, d2, d3}
print(len(dogs)) # 2(d1 和 d2 内容相同,集合去重)
第四部分:继承的完整用法
4.1 单继承
# ===== 父类(基类、超类)=====
class Animal:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def eat(self):
print(f"{self.name} 正在吃东西")
def sleep(self):
print(f"{self.name} 正在睡觉")
def __str__(self):
return f"{self.__class__.__name__}({self.name},{self.age}岁)"
# ===== 子类继承父类 =====
class Dog(Animal): # Dog 继承自 Animal(括号里写父类名)
"""狗类,继承动物类"""
# 子类可以添加自己独有的方法
def bark(self):
print(f"{self.name} 汪汪叫!")
def fetch(self):
print(f"{self.name} 捡回来了球!")
class Cat(Animal): # Cat 也继承自 Animal
"""猫类,继承动物类"""
def meow(self):
print(f"{self.name} 喵喵叫~")
def purr(self):
print(f"{self.name} 呼噜呼噜...")
# ===== 使用 =====
dog = Dog("旺财", 3)
cat = Cat("咪咪", 2)
# 继承自 Animal 的方法(不需要重新写)
dog.eat() # 旺财 正在吃东西
dog.sleep() # 旺财 正在睡觉
cat.eat() # 咪咪 正在吃东西
# 子类自己的方法
dog.bark() # 旺财 汪汪叫!
cat.meow() # 咪咪 喵喵叫~
# __str__ 继承自 Animal
print(dog) # Dog(旺财,3岁)
print(cat) # Cat(咪咪,2岁)
# 类型检查
print(isinstance(dog, Dog)) # True(是 Dog 的实例)
print(isinstance(dog, Animal)) # True(也是 Animal 的实例)
print(isinstance(dog, Cat)) # False(不是 Cat 的实例)
print(issubclass(Dog, Animal)) # True(Dog 是 Animal 的子类)
print(issubclass(Cat, Animal)) # True
print(issubclass(Dog, Cat)) # False(Dog 不是 Cat 的子类)
print(issubclass(Dog, object)) # True(所有类都是 object 的子类)
4.2 子类覆盖父类方法
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name} 发出了声音")
def describe(self):
print(f"我是 {self.name},一只{self.__class__.__name__}")
class Dog(Animal):
# ===== 覆盖(Override)父类的 speak 方法 =====
def speak(self):
# 完全替换父类的实现
print(f"{self.name} 汪汪!汪汪!")
class Cat(Animal):
def speak(self):
print(f"{self.name} 喵~")
class Duck(Animal):
def speak(self):
print(f"{self.name} 嘎嘎嘎!")
class Mute(Animal):
# 不覆盖 speak,使用父类的默认实现
pass
# ===== 多态:同一个方法名,不同类有不同行为 =====
animals = [
Dog("旺财"),
Cat("咪咪"),
Duck("唐老鸭"),
Mute("哑巴"),
]
for animal in animals:
animal.speak() # 调用各自的 speak,行为不同
# 输出:
# 旺财 汪汪!汪汪!
# 咪咪 喵~
# 唐老鸭 嘎嘎嘎!
# 哑巴 发出了声音 ← 使用父类默认实现
4.3 super()——调用父类的方法
class Animal:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
print(f"Animal.__init__ 执行:name={name}, age={age}")
def speak(self):
print(f"{self.name} 发出声音")
def info(self):
return f"{self.name},{self.age}岁"
class Dog(Animal):
def __init__(self, name: str, age: int, breed: str):
# ===== super() 调用父类的 __init__ =====
# 不用重复写 self.name = name, self.age = age
super().__init__(name, age)
# 再添加子类自己的属性
self.breed = breed
print(f"Dog.__init__ 执行:breed={breed}")
def speak(self):
# ===== super() 调用父类的 speak,再添加子类行为 =====
super().speak() # 先执行父类的 speak
print(f"({self.breed}犬)汪汪!")
def info(self):
# 复用父类的 info,再补充额外信息
base_info = super().info()
return f"{base_info},品种:{self.breed}"
d = Dog("旺财", 3, "金毛")
# 执行顺序:
# Animal.__init__ 执行:name=旺财, age=3
# Dog.__init__ 执行:breed=金毛
d.speak()
# 旺财 发出声音 ← 父类的 speak
# (金毛犬)汪汪! ← 子类添加的
print(d.info())
# 旺财,3岁,品种:金毛 ← 父类 info + 子类补充
# ===== 常见错误:忘记调用 super().__init__() =====
class BadDog(Animal):
def __init__(self, name, age, breed):
# ❌ 没有调用 super().__init__()
self.breed = breed
# 结果:self.name 和 self.age 未定义!
# bad = BadDog("旺财", 3, "金毛")
# bad.speak() # AttributeError: 'BadDog' object has no attribute 'name'
4.4 多继承
# ===== 多继承:一个类同时继承多个父类 =====
class Flyable:
"""会飞的能力"""
def fly(self):
print(f"{self.name} 正在飞翔!")
def land(self):
print(f"{self.name} 降落了")
class Swimmable:
"""会游泳的能力"""
def swim(self):
print(f"{self.name} 正在游泳!")
def dive(self):
print(f"{self.name} 潜水中...")
class Animal:
def __init__(self, name):
self.name = name
def breathe(self):
print(f"{self.name} 呼吸")
# 鸭子:既能飞,又能游泳
class Duck(Animal, Flyable, Swimmable):
def quack(self):
print(f"{self.name} 嘎嘎嘎!")
# 海鸥:只能飞,不能游泳
class Seagull(Animal, Flyable):
def cry(self):
print(f"{self.name} 啊~")
duck = Duck("唐老鸭")
duck.breathe() # 唐老鸭 呼吸
duck.fly() # 唐老鸭 正在飞翔!
duck.swim() # 唐老鸭 正在游泳!
duck.dive() # 唐老鸭 潜水中...
duck.quack() # 唐老鸭 嘎嘎嘎!
# 查看继承关系
print(Duck.__bases__)
# (<class '__main__.Animal'>, <class '__main__.Flyable'>, <class '__main__.Swimmable'>)
print(isinstance(duck, Animal)) # True
print(isinstance(duck, Flyable)) # True
print(isinstance(duck, Swimmable)) # True
第五部分:MRO 方法解析顺序
5.1 什么是 MRO?
# MRO = Method Resolution Order(方法解析顺序)
# 当调用一个方法时,Python 按照 MRO 的顺序依次查找
class A:
def hello(self):
print("A.hello")
class B(A):
def hello(self):
print("B.hello")
class C(A):
def hello(self):
print("C.hello")
class D(B, C): # 多继承,同时继承 B 和 C
pass
d = D()
d.hello() # B.hello(按 MRO 找到 B.hello 后就停止)
# 查看 MRO
print(D.__mro__)
# (<class '__main__.D'>,
# <class '__main__.B'>,
# <class '__main__.C'>,
# <class '__main__.A'>,
# <class 'object'>)
# 也可以这样查看(更直观)
print(D.mro())
# [<class '__main__.D'>, <class '__main__.B'>,
# <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
5.2 MRO 规则可视化
object
/
A
/
B C
/
D
MRO 规则(C3 线性化算法):
① 先从自身开始
② 按继承列表从左到右
③ 深度优先,但确保父类在子类之后
④ object 永远在最后
D 的 MRO:D → B → C → A → object
查找 hello 时:
1. D 没有 hello → 继续
2. B 有 hello → 找到!执行 B.hello,停止
5.3 super() 与 MRO 的关系
class A:
def greet(self):
print("A greet")
# super() 在这里是 object,没有 greet,所以不调用
class B(A):
def greet(self):
print("B greet 开始")
super().greet() # 按 MRO 找下一个,是 A
print("B greet 结束")
class C(A):
def greet(self):
print("C greet 开始")
super().greet() # 按 MRO 找下一个,是 A
print("C greet 结束")
class D(B, C):
def greet(self):
print("D greet 开始")
super().greet() # 按 MRO 找下一个,是 B
print("D greet 结束")
# D 的 MRO:D → B → C → A → object
d = D()
d.greet()
# 输出:
# D greet 开始
# B greet 开始 ← super() 从 D 找到 B
# C greet 开始 ← super() 从 B 继续找到 C(不是 A!MRO 决定的)
# A greet ← super() 从 C 继续找到 A
# C greet 结束
# B greet 结束
# D greet 结束
# 关键:super() 不是"调用父类",而是"按 MRO 找下一个"
第六部分:各种写法的完整对比
6.1 四种写法的完整对比
# ===== 写法1:class Dog: =====
class Dog1:
pass
# ===== 写法2:class Dog(): =====
class Dog2():
pass
# ===== 写法3:class Dog(object): =====
class Dog3(object):
pass
# ===== 写法4:继承其他类 =====
class Animal:
def __init__(self, name):
self.name = name
class Dog4(Animal): # 继承 Animal(同时也间接继承 object)
pass
# ===== 全面对比 =====
for Cls in [Dog1, Dog2, Dog3]:
print(f"n{Cls.__name__}:")
print(f" __bases__ = {Cls.__bases__}") # 直接父类
print(f" __mro__ = {[c.__name__ for c in Cls.__mro__]}")
print(f"nDog4:")
print(f" __bases__ = {Dog4.__bases__}")
print(f" __mro__ = {[c.__name__ for c in Dog4.__mro__]}")
# 输出:
# Dog1:
# __bases__ = (<class 'object'>,)
# __mro__ = ['Dog1', 'object']
#
# Dog2:
# __bases__ = (<class 'object'>,)
# __mro__ = ['Dog2', 'object']
#
# Dog3:
# __bases__ = (<class 'object'>,)
# __mro__ = ['Dog3', 'object']
#
# Dog4:
# __bases__ = (<class '__main__.Animal'>,)
# __mro__ = ['Dog4', 'Animal', 'object']
6.2 type() 函数的行为
class Dog:
pass
class GoldenRetriever(Dog):
pass
d = Dog()
gr = GoldenRetriever()
# type():返回对象的直接类型(不考虑继承)
print(type(d)) # <class '__main__.Dog'>
print(type(gr)) # <class '__main__.GoldenRetriever'>
# type() vs isinstance() 的区别
print(type(gr) == Dog) # False(直接类型是 GoldenRetriever)
print(isinstance(gr, Dog)) # True(是 Dog 的实例,考虑继承链)
print(isinstance(gr, object)) # True(所有对象都是 object 的实例)
# 实际开发建议:
# 用 isinstance 而不是 type() ==,因为 isinstance 考虑继承
第七部分:括号里到底能写什么?
7.1 括号里的各种情况
# ===== 情况1:括号为空 = 继承 object =====
class A():
pass
print(A.__bases__) # (<class 'object'>,)
# ===== 情况2:显式写 object =====
class B(object):
pass
print(B.__bases__) # (<class 'object'>,)
# ===== 情况3:继承自定义类 =====
class C(A):
pass
print(C.__bases__) # (<class '__main__.A'>,)
# ===== 情况4:多继承 =====
class Mixin1:
def method1(self):
print("Mixin1.method1")
class Mixin2:
def method2(self):
print("Mixin2.method2")
class Base:
def base_method(self):
print("Base.base_method")
class MyClass(Base, Mixin1, Mixin2): # 多继承(逗号分隔)
pass
obj = MyClass()
obj.base_method() # Base.base_method
obj.method1() # Mixin1.method1
obj.method2() # Mixin2.method2
print(MyClass.__mro__)
# [MyClass, Base, Mixin1, Mixin2, object]
# ===== 情况5:内置类型作为父类 =====
class MyList(list): # 继承内置 list
def sum(self):
return sum(self)
class MyDict(dict): # 继承内置 dict
def keys_list(self):
return list(self.keys())
class MyStr(str): # 继承内置 str
def word_count(self):
return len(self.split())
ml = MyList([1, 2, 3, 4, 5])
ml.append(6) # 继承自 list 的方法
print(ml.sum()) # 21(自定义方法)
md = MyDict({"a": 1, "b": 2})
print(md.keys_list()) # ['a', 'b']
ms = MyStr("hello world python")
print(ms.upper()) # HELLO WORLD PYTHON(继承 str)
print(ms.word_count()) # 3(自定义)
# ===== 情况6:动态基类(高级用法)=====
def get_base():
return list # 根据条件动态选择父类
DynamicClass = type("DynamicClass", (get_base(),), {})
obj2 = DynamicClass([1, 2, 3])
print(obj2) # [1, 2, 3]
第八部分:常见疑问解答
8.1 疑问1:空括号 class Dog(): 有没有实际意义?
# 从功能上说:完全没有区别
# 但有些人认为括号提供了视觉上的一致性
# ===== 代码风格建议 =====
# ✅ Python 3 推荐写法(PEP 8)
class Dog:
pass
# ✅ 也可以接受
class Dog(object):
pass
# ❌ 不推荐(无意义的空括号)
class Dog(): # PEP 8 不建议这样写,空括号毫无意义
pass
# PEP 8 官方建议:
# "For new code Guido recommends against using class Foo(object) in Python 3."
# 对于新代码,Guido(Python 之父)推荐不要在 Python 3 中使用 class Foo(object)
8.2 疑问2:如何判断一个类是否继承了某个类?
class Animal:
pass
class Dog(Animal):
pass
class GoldenRetriever(Dog):
pass
# issubclass:检查类的继承关系
print(issubclass(GoldenRetriever, Dog)) # True(直接父类)
print(issubclass(GoldenRetriever, Animal)) # True(间接父类)
print(issubclass(GoldenRetriever, object)) # True(所有类都是 object 的子类)
print(issubclass(Dog, GoldenRetriever)) # False(不能反过来)
# isinstance:检查对象的类型(考虑继承链)
gr = GoldenRetriever()
print(isinstance(gr, GoldenRetriever)) # True
print(isinstance(gr, Dog)) # True
print(isinstance(gr, Animal)) # True
print(isinstance(gr, object)) # True
# 查看直接父类
print(GoldenRetriever.__bases__) # (<class '__main__.Dog'>,)
print(Dog.__bases__) # (<class '__main__.Animal'>,)
print(Animal.__bases__) # (<class 'object'>,)
print(object.__bases__) # () ← object 没有父类
# 查看完整继承链
print(GoldenRetriever.__mro__)
# (<class '__main__.GoldenRetriever'>,
# <class '__main__.Dog'>,
# <class '__main__.Animal'>,
# <class 'object'>)
8.3 疑问3:子类可以访问父类的私有属性吗?
class Animal:
def __init__(self, name):
self.name = name # 公有属性(子类可直接访问)
self._age = 0 # 约定受保护(子类可以访问,但不建议外部访问)
self.__dna = "ATCG" # 私有属性(Python 名称改写,子类无法直接访问)
def get_dna(self):
return self.__dna # 父类可以通过方法暴露
class Dog(Animal):
def show_info(self):
print(self.name) # ✅ 公有属性,可以访问
print(self._age) # ✅ 受保护属性,可以访问(不推荐)
# print(self.__dna) # ❌ AttributeError!
# 因为 Python 把 __dna 改写成了 _Animal__dna
print(self._Animal__dna) # ✅ 强行访问(非常不推荐!)
def get_parent_dna(self):
return self.get_dna() # ✅ 通过父类的公有方法访问
d = Dog("旺财")
d.show_info()
print(d.get_parent_dna()) # ATCG
# 名称改写演示
print(dir(d)) # 可以看到 _Animal__dna 这个被改写的名字
8.4 疑问4:什么时候用继承,什么时候用组合?
# ===== 继承(is-a 关系)=====
# 子类"是一种"父类
# Dog "is a" Animal → ✅ 适合继承
class Animal:
def breathe(self):
print("呼吸")
class Dog(Animal): # Dog is-a Animal → 用继承
def bark(self):
print("汪汪")
# ===== 组合(has-a 关系)=====
# 对象"有一个"另一个对象
# Dog "has a" Tail → ✅ 适合组合(不是继承!)
class Tail:
def __init__(self, length):
self.length = length
def wag(self):
print(f"摇动{self.length}厘米的尾巴")
class Engine:
def start(self):
print("引擎启动")
def stop(self):
print("引擎停止")
class Car:
def __init__(self):
self.engine = Engine() # Car has-a Engine → 用组合
self.speed = 0
def start(self):
self.engine.start()
print("汽车发动了")
def stop(self):
self.engine.stop()
print("汽车停了")
# 组合的优点:
# ① 更灵活(可以替换组件,如换个不同的引擎)
# ② 避免继承的复杂性(多继承的 MRO 问题)
# ③ 符合"组合优于继承"的设计原则(Design Principle)
car = Car()
car.start() # 引擎启动 n 汽车发动了
# ===== 实际判断技巧 =====
# 问自己:
# "XX 是一种 YY 吗?" → 是 → 用继承
# "XX 有一个 YY 吗?" → 是 → 用组合
# 例子:
# 经理 是一种 员工? → 是 → class Manager(Employee)
# 公司 有一个 员工吗? → 是 → class Company: self.employees = []
# 狗 是一种 动物? → 是 → class Dog(Animal)
# 狗 有一条 尾巴吗? → 是 → class Dog: self.tail = Tail()
第九部分:Mixin 模式
9.1 Mixin 是什么?
# Mixin:一种特殊的多继承用法
# 目的:给类"混入"一些额外功能,而不是建立 is-a 关系
# ===== 定义 Mixin 类 =====
class JsonMixin:
"""给任意类添加 JSON 序列化能力"""
def to_json(self):
import json
return json.dumps(self.__dict__, ensure_ascii=False, indent=2)
@classmethod
def from_json(cls, json_str):
import json
data = json.loads(json_str)
obj = cls.__new__(cls)
obj.__dict__.update(data)
return obj
class LogMixin:
"""给任意类添加日志能力"""
def log(self, message: str):
import datetime
ts = datetime.datetime.now().strftime("%H:%M:%S")
print(f"[{ts}] [{self.__class__.__name__}] {message}")
class ValidateMixin:
"""给任意类添加验证能力"""
def validate(self):
for attr, value in self.__dict__.items():
if value is None:
raise ValueError(f"属性 {attr} 不能为 None")
return True
# ===== 使用 Mixin =====
class User(JsonMixin, LogMixin, ValidateMixin):
def __init__(self, name, email, age):
self.name = name
self.email = email
self.age = age
class Product(JsonMixin, LogMixin):
def __init__(self, title, price, stock):
self.title = title
self.price = price
self.stock = stock
user = User("张三", "zhangsan@test.com", 25)
# 使用 JsonMixin 的功能
print(user.to_json())
# 使用 LogMixin 的功能
user.log("用户登录成功")
# 使用 ValidateMixin 的功能
print(user.validate()) # True
# 从 JSON 还原
json_str = user.to_json()
user2 = User.from_json(json_str)
print(user2.name) # 张三
# Product 也能用 JSON 和 Log 功能
p = Product("Python入门书", 59.9, 100)
print(p.to_json())
p.log("商品已上架")
# Mixin 命名规范:通常以 Mixin 结尾
# MRO 中 Mixin 类通常放在前面(Base 类在最后)
第十部分:总结与最佳实践
10.1 一图总结
写法对比(Python 3):
class Dog: ← 隐式继承 object,✅ 推荐写法
class Dog(): ← 隐式继承 object,❌ 空括号无意义
class Dog(object): ← 显式继承 object,⚠️ 冗余但无害
class Dog(Animal): ← 继承 Animal(同时间接继承 object)
继承关系:
object
└── Animal
└── Dog
└── GoldenRetriever
所有类最终都继承自 object。
查看继承关系的工具:
Cls.__bases__ → 直接父类(元组)
Cls.__mro__ → 完整继承链(元组)
Cls.mro() → 完整继承链(列表)
isinstance(obj, Cls) → 对象是否是类的实例(考虑继承)
issubclass(A, B) → A 是否是 B 的子类(考虑继承)
type(obj) → 对象的直接类型(不考虑继承)
10.2 实际开发建议
# ===== ✅ 推荐的写法 =====
# 1. Python 3 中用简洁写法
class Animal:
pass
# 2. 继承父类时明确写出父类名
class Dog(Animal):
pass
# 3. 子类 __init__ 中调用 super().__init__()
class GoldenRetriever(Dog):
def __init__(self, name, age, color):
super().__init__(name, age) # 不忘调用父类初始化
self.color = color
# 4. 用 isinstance 而不是 type() ==
obj = GoldenRetriever("旺财", 3, "金色")
if isinstance(obj, Dog): # ✅
print("是狗")
if type(obj) == Dog: # ❌ 不推荐(子类不通过)
print("是狗(这里不会打印,因为 type 是 GoldenRetriever)")
# 5. 多继承时,Mixin 放前面,主类放后面
class MyView(LoginRequiredMixin, PermissionMixin, BaseView):
pass
# ===== ❌ 避免的写法 =====
# 错误1:忘记 super().__init__()
class BadDog(Animal):
def __init__(self, name, breed):
# super().__init__(name) ← 忘记了!
self.breed = breed
# 后果:name 属性未初始化,调用父类方法会报错
# 错误2:多继承时菱形继承不用 super()
class A:
def hello(self):
print("A")
class B(A):
def hello(self):
A.hello(self) # ❌ 直接调用,而不用 super()
print("B")
class C(A):
def hello(self):
A.hello(self) # ❌ 同上
print("C")
class D(B, C):
def hello(self):
B.hello(self) # ❌ A.hello 会被调用两次!
C.hello(self)
print("D")
# 错误3:为了代码复用滥用继承(应该用组合)
# class PDFParser(ReportGenerator): pass ← PDF解析器 is-a 报表生成器?不合理!
# class Order(User): pass ← 订单 is-a 用户?不合理!
10.3 速查表
📌 类的定义方式(Python 3 中三种等价)
class Foo: → 推荐
class Foo(): → 不推荐(空括号无意义)
class Foo(object): → 可接受(显式写出 object)
📌 继承
class Child(Parent): → 单继承
class Child(P1, P2, P3): → 多继承(逗号分隔)
super().__init__(...) → 调用父类 __init__
super().method() → 调用父类方法
📌 查看继承关系
Cls.__bases__ → 直接父类(元组)
Cls.__mro__ 或 Cls.mro() → 完整继承链
isinstance(obj, Cls) → 是否是实例(含子类)
issubclass(SubCls, Cls) → 是否是子类
📌 object 提供的默认方法
__str__ → str(obj) 或 print(obj) 时调用
__repr__ → repr(obj) 时调用
__eq__ → == 比较
__ne__ → != 比较
__hash__ → hash(obj)
__class__ → 所属类
__dir__ → dir(obj)
📌 继承 vs 组合
is-a 关系(狗是动物) → 用继承 class Dog(Animal)
has-a 关系(狗有尾巴) → 用组合 self.tail = Tail()
📌 Mixin 模式
class MyClass(Mixin1, Mixin2, Base):
pass
Mixin 不建立 is-a 关系,只是混入额外功能
总结
核心要点只有三句话:
1. 在 Python 3 中,class Dog: / class Dog(): / class Dog(object): 完全相同,推荐用 class Dog:。
2. 所有类(包括你不写括号的类)都自动继承 object,object 提供了 __str__、__eq__、__hash__ 等基础魔术方法。
3. 括号里有内容时(如 class Dog(Animal):),就是在声明继承关系——Dog 是 Animal 的子类,自动拥有父类的所有方法和属性,同时仍然间接继承 object。
# 一句话验证:
print(object in Dog.__mro__) # True,无论怎么写类
# 终极验证:
class A: pass
class B(): pass
class C(object): pass
class D(A): pass
for Cls in [A, B, C, D]:
print(f"{Cls.__name__}: object 在 MRO 中?{object in Cls.__mro__}")
# A: object 在 MRO 中?True
# B: object 在 MRO 中?True
# C: object 在 MRO 中?True
# D: object 在 MRO 中?True