22.Python class Dog: 与 class Dog(): 的区别——彻底搞懂类的继承与 object 基类

Python class Dog: 与 class Dog(): 的区别——彻底搞懂类的继承与 object 基类

本文档面向零基础新手,目标是让你真正理解:

  • class Dog:class Dog(): 到底有没有区别
  • Python 2 和 Python 3 的历史背景
  • 什么是 object,为什么它是所有类的基类
  • 显式继承 vs 隐式继承的写法差异
  • 单继承、多继承的写法
  • isinstanceissubclasstype() 的行为
  • 继承链(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. 所有类(包括你不写括号的类)都自动继承 objectobject 提供了 __str____eq____hash__ 等基础魔术方法。

3. 括号里有内容时(如 class Dog(Animal):),就是在声明继承关系——DogAnimal 的子类,自动拥有父类的所有方法和属性,同时仍然间接继承 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

发表评论