🌟想系统化学习 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'>]