Python 学习方法 | 追根溯源 — 获取对象的继承结构

🌟想系统化学习 GUI 编程?看看这个:[Python GUI 编程] PySide & PyQt - 学习手册-CSDN博客

本章不涉及啥具体的知识点,只是提供一个 Python 框架的学习思路,我称其为 “追根溯源”。

0x01:Python 继承 & 泛化示例

Python 是一门面向对象编程的语言,面向对象编程有一个核心机制 —— 继承与泛化。下面是一个简单的例子,Animal 类与 Dog 类,Dog 类继承自 Animal 类:

class Animal:
    def __init__(self, name):
        self.name = name
​
    def get_name(self):
        return self.name
    
    def speak(self):
        print(f"{self.name} 正在发出声音")
    
    def run(self):
        print(f"{self.name} 正在奔跑")
​
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)
    
    def speak(self):
        print(f"{self.name} => 汪汪汪!")

上面的代码逻辑十分简单,属于是 Python 面向对象基础级的语言。然而我们实际工作时,遇到的可不是这么 Easy 的代码,比如 PyQt、PySide,它们的继承结构和代码量就非常庞大,那么我要怎么才能完美掌握它们呢。

笔者这里的建议,是使用思维导图,画一份继承结构图,以上面的代码为例,我们可以通过 XMind 整理出以下几张图(XMind 的内置链接功能):

笔者提供的那个例子比较 Easy,我们可以一眼理清类的属性、方法,以及继承关系,但实际编程时用的那些类都是贼拉长的,分布于各个文件中,那么此时,我们要怎么去获取类的继承结构呢?

笔者这里提供了两种方法,一种是通过常见的代码编辑器,一种是通过 Python 提供的函数。

0x02:代码编辑器获取对象继承结构

我们通过在代码编辑器中按住 Ctrl 加点击你想要追溯的类名,编辑器就会自动追溯,下面是以 PyQt 中的 QLabel 组件为例的一次追溯:

# => 代表派生,下面的意思是 QFrame 派生出了 QLabel 类, Label 类具有 QFrame 的所有特性
QFrame => QLabel

QWidget => QFrame => QLabel

QtCore.QObject => QWidget => QFrame => QLabel

如上,我们一直从 QLabel 类追踪到了其祖宗类 QObject 类,可见其继承(抽象程度)之深,如果我们想要学好 QLabel 类,那么就得掌握好 QObject 类中的方法、然后还得掌握好 QWidget 类中的方法,然后是 QFrame 中的,最后才是 QLabel 自己的特殊方法(学的东西还是很多的)。

0x03:Python 函数获取对象的继承结构

上面那种通过代码编辑器点点的方式是比较 Easy,但是只适用逆推,即推测该类的父类是啥,没办法正推。比如,给你一个 Animal 类,我问你,从它派生出来的子类有哪些,你是不知道的。

0x0301:从子类追踪父类 - mro() 方法

Python 提供了 mro() 方法(Method Resolution Order,方法解析顺序)用于确定继承关系中方法的调用顺序。它定义了当调用一个类的方法或属性时,Python 解释器按照怎样的顺序在继承链中搜索该成员。直接看下面这个例子:

class A: pass
class B(A): pass # B 继承 A
class C(A): pass # C 继承 A
class D(B): pass # D 继承 B
class E(D, C): pass # E 继承 D 和 C
​
print(A.mro()) # 获取 A 的方法解析顺序
# [<class '__main__.A'>, <class 'object'>]
print(D.mro()) # 获取 D 的方法解析顺序,也可以写成 D.__mro__
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
print(E.mro()) # 获取 E 的方法解析顺序
# [<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

下面是代码的继承图,用于辅助理解,希望读者仔细看看其解析顺序,特别是 E 和 D 的:

0x0302:从父类追踪子类 - __subclasses__() 方法

Python 中的 __subclasses__ 方法用于 动态获取一个类的直接子类,它属于类对象的内省(introspection)工具,能够反映当前运行时类的继承关系图。直接看下面例子:

class A: pass
class B(A): pass # B 继承 A
class C(A): pass # C 继承 A
class D(B): pass # D 继承 B
class E(D, C): pass # E 继承 B 和 C
​
print(A.__subclasses__()) # [<class '__main__.B'>, <class '__main__.C'>]
print(D.__subclasses__()) # [<class '__main__.E'>]

虽然 __subclasses__() 方法默认只能追踪一级,但是写个 For 循环不就可以一直追踪出某个类下派生出来的所有子类了嘛,看下面程序的运行结果:

class A: pass
class B(A): pass # B 继承 A
class C(A): pass # C 继承 A
class D(B): pass # D 继承 B
class E(D, C): pass # E 继承 B 和 C
​
def trace_subclass(cls):
    """
    子类追踪函数,获取该类的派生出来的所有子类
    @param cls: 用于被追踪的类
    @return: [], 返回该类派生出来的所有子类
    """
    subclass_list = []
    # 遍历当前类的所有子类
    for subclass in cls.__subclasses__():
        # 将当前子类添加到列表中
        subclass_list.append(subclass)
        # 递归追踪当前子类的子类
        subclass_list += trace_subclass(subclass)
    return list(set(subclass_list)) # 去重
​
print(trace_subclass(A)) # 追踪 A 派生出来的所有子类
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blue17 :: Hack3rX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值