Skip to content

六、面向对象(P53–P60) ​

P53 · 知识点:构造方法 ​

【题目】Python 类中用于初始化实例的方法是? A. init B. __construct C. init D. constructor

答案:A

【考点】init 实例初始化方法(构造后自动调用)。

【结论】选 A。Python 类中用于初始化实例的魔法方法是 init,在对象创建完成后自动调用以设置实例属性。

【逐项辨析】

  • A. init:正确。init(self, ...) 是 Python 的实例初始化方法,在 new 创建对象后自动调用,用于绑定实例属性。调用类名(...)时,解释器自动触发 new 和 init。
  • B. __construct:错误。这是 PHP 的构造方法命名,Python 不使用此名称。
  • C. init:错误。缺少双下划线的普通名称不是魔法方法,不会被解释器自动识别和调用。
  • D. constructor:错误。这是 Java、C++ 等语言的构造方法通用术语,Python 中对应概念由 new(创建)和 init(初始化)共同完成。

【知识点】在 Python 的对象创建流程中,new(cls, ...) 负责实际分配内存并返回实例(真正的“构造”),init(self, ...) 负责初始化实例状态。new 是类方法(第一个参数是 cls),而 init 是实例方法(第一个参数是 self)。绝大多数类只需重写 init,只有涉及不可变子类(如 int、tuple 子类)或元类编程时才需要重写 new。init 不能返回非 None 值,否则会触发 TypeError。双下划线方法(dunder methods)是 Python 数据模型的核心,使得自定义对象可以响应内置函数和运算符。

【记忆锚点】“双下划线 init,构造完毕就执行;没有双下不魔法,PHP 用 construct 要区分。”

【易混对比】

概念方法名调用时机是否必须重写返回值限制
创建对象new(cls)实例化第一步极少必须返回实例
初始化对象init(self)new 之后自动调用经常必须为 None
PHP 构造__construct()new 时调用按需无特殊限制

换问法:若问“Python 中真正负责创建并返回实例对象的方法是?”,则答案变为 new。

【自测】

以下代码输出什么?

python
class Demo:
    def __new__(cls):
        print("new")
        return super().__new__(cls)
    def __init__(self):
        print("init")
d = Demo()

答:先输出 "new",再输出 "init"。因为实例化时先调用 new 创建对象,再调用 init 初始化。与第 P56 题连考。

【知识关联】

  • 同库关联:与 P54(self)、P58(魔法方法);__init__ 是初始化不是分配。
  • 实现层:__new__ 分配,__init__ 初始化;默认 object.new。
  • 面试追问:① __init__ 能否返回值?② 与 Java 构造器差异?

【拓展延伸】

  • 变式问法:继承时是否自动调父类 init(否,需 super)。
  • 版本差异:语义稳定。
  • 工程注意点:复杂对象可用 dataclass 生成。

P54 · 知识点:self ​

【题目】实例方法的第一个参数按约定命名为? A. this B. self C. me D. cls

答案:B

【考点】self 与 cls 的命名约定。

【结论】选 B。Python 实例方法的第一个位置参数按约定命名为 self,代表调用该方法的实例对象本身。

【逐项辨析】

  • A. this:错误。this 是 Java、C++、JavaScript 等语言中表示当前实例的关键字;Python 中没有 this 关键字,使用 self 作为约定名称。
  • B. self:正确。这是 Python 社区强烈约定的命名规范,虽然 self 不是保留字,但几乎所有代码都遵循此约定,否则可读性极差。
  • C. me:错误。某些语言(如 VB)使用 me,Python 中无此约定,若用 me 会被视为不符合规范的参数名。
  • D. cls:错误。cls 是类方法(@classmethod)的第一个参数约定名称,代表类本身而非实例。

【知识点】Python 的方法调用机制是显式传递实例/类对象:当调用 obj.method(arg) 时,解释器实际转换为 MyClass.method(obj, arg)。因此实例方法必须定义至少一个位置参数来接收该实例引用,这个参数按约定叫 self。同理,@classmethod 装饰的方法调用 Cls.method(arg) 实际转换为 MyClass.method(Cls, arg),第一个参数约定叫 cls。self 和 cls 都不是关键字,理论上可以改名,但强烈不建议。如果忘记写 self,调用实例方法时会报 "takes 0 positional arguments but 1 was given" 的错误,因为解释器自动传入了实例却找不到形参接收。

【记忆锚点】“实例 self,类用 cls;this 是 Java,me 是 VB;约定不遵守,排错找半天。”

【易混对比】

方法类型第一个参数约定装饰器调用方式参数代表
实例方法self无obj.method()实例对象
类方法cls@classmethodCls.method() 或 obj.method()类对象
静态方法无特殊要求@staticmethod任意方式调用无自动传入参数

换问法:若问“类方法的第一个参数按约定应命名为?”,则答案变为 cls。

【自测】

以下代码会报错吗?为什么?

python
class A:
    def show():
        print("hi")
a = A()
a.show()

答:会报错 TypeError: A.show() takes 0 positional arguments but 1 was given。因为 a.show() 会隐式把 a 作为第一个参数传入,但 show() 没有定义 self 参数来接收。与第 P53 题连考。

【知识关联】

  • 同库关联:与 P53/P56;self 是约定名不是关键字。
  • 实现层:方法定义时第一个参数,调用时自动传入实例。
  • 面试追问:① 能否不叫 self?② 类方法/静态方法差异?

【拓展延伸】

  • 变式问法:@staticmethod 无 self;@classmethod 传 cls。
  • 版本差异:语义稳定。
  • 工程注意点:始终写 self 提高可读性;不要手动传。

P55 · 知识点:继承 ​

【题目】class B(A): 表示?

A. A 继承 B B. B 继承 A C. B 实现 A 接口 D. 组合关系

答案:B

【考点】类定义括号中列出父类。

【结论】选 B。class B(A): 表示 B 继承自 A,B 是子类(派生类),A 是父类(基类)。

【逐项辨析】

  • A. A 继承 B:错误。继承方向颠倒,括号外的是子类,括号内的是父类。
  • B. B 继承 A:正确。B 获得 A 的所有非私有属性和方法,可以重写或扩展。
  • C. B 实现 A 接口:错误。Python 中没有 interface 关键字,类定义括号内是继承关系而非接口实现关系(虽然抽象基类 ABC 可以模拟接口概念)。
  • D. 组合关系:错误。组合通常表现为一个类的实例作为另一个类的属性(如 class B: def init(self): self.a = A()),而非继承语法。

【知识点】Python 支持多重继承,语法为 class C(A, B):,方法解析顺序(MRO)采用 C3 线性化算法,可通过 ClassName.mro 或 mro() 查看。子类可以重写父类方法,通过 super() 调用被重写的父类方法。Python 3 中所有类默认继承自 object(新式类)。继承是“is-a”关系,组合是“has-a”关系,设计时应优先使用组合以保持低耦合。检查继承关系使用内置函数 issubclass(B, A) 和 isinstance(obj, A)。

【记忆锚点】“括号外面是儿子,括号里面是爸爸;B 把 A 括起来,B 继承 A 没毛病。”

【易混对比】

关系类型语法表现语义设计原则
继承class B(A)is-a 关系强耦合,慎用深继承链
组合self.a = A()has-a 关系低耦合,优先推荐
实现接口class B(A)(A 是 ABC)必须实现抽象方法用 @abstractmethod 约束
依赖方法参数传入临时使用最弱耦合

换问法:若问“Python 中 class C(A, B) 的方法按什么顺序查找?”,则答案涉及 MRO/C3 线性化。

【自测】

以下代码的输出顺序是什么?

python
class A:
    def f(self): print("A")
class B(A):
    def f(self): print("B")
class C(A):
    def f(self): print("C")
class D(B, C):
    pass
D().f()

答:输出 "B"。按 MRO(D→B→C→A→object)查找,B 在 C 前面先找到。与第 P56 题连考。

【知识关联】

  • 同库关联:与 P56(super)、P57(多态)、J13/J12 对照。
  • 实现层:MRO C3 线性化;多继承方法解析顺序。
  • 面试追问:① MRO 是什么?② 菱形继承?

【拓展延伸】

  • 变式问法:class C(A,B) 查找顺序。
  • 版本差异:Py3 统一新式类与 MRO。
  • 工程注意点:避免复杂多继承;优先组合。

P56 · 知识点:super ​

【题目】子类的 init 中调用父类初始化方法,正确写法是? A. 父类名().init() B. parent.init() C. super().init() D. super.init()

答案:C

【考点】super() 调用父类方法(Python 3 语法)。

【结论】选 C。在 Python 3 中,子类通过 super().init() 调用父类的初始化方法,super() 返回一个代理对象用于委托方法调用。

【逐项辨析】

  • C. super().init():正确。Python 3 的零参数 super() 会自动根据当前类和实例推断出正确的父类和方法解析顺序,简洁且支持多重继承。
  • B. parent.init():错误。parent 不是内置关键字,且 init 缺少双下划线,完全不是合法的父类调用方式。
  • A. 父类名().init():错误。首先父类名()会创建一个父类的新实例而非操作子类实例,其次 init 不是 init,且即使写成 Parent.init(self) 虽可行但不推荐(硬编码类名、不支持多继承协作)。
  • D. super.init():错误。super 是内置函数/类,必须加括号调用 super() 才能返回代理对象,super.init 只是访问 super 这个类的 init 属性,语法完全错误。

【知识点】super() 是 Python 实现合作式多重继承的关键工具。Python 3 中无参 super() 等价于 Python 2 中的 super(当前类名, self)。super() 不是简单地调用“直接父类”,而是遵循 MRO 顺序调用下一个类的方法,因此在复杂钻石继承中能保证每个类的方法只被调用一次。如果子类重写了 init,父类的 init 不会自动执行,必须显式调用 super().init() 来确保父类侧的属性正确初始化。在多重继承场景中,硬编码父类名(如 Parent.init(self))会破坏 super() 的协作链,应始终优先使用 super()。

【记忆锚点】“super() 带括号,再找 init;没括号是找属性,调用肯定报错误。”

【易混对比】

写法语法正确性是否推荐支持多重继承说明
super().init()正确强烈推荐是Python 3 零参数,遵循 MRO
super(Child, self).init()正确Python 2 兼容用是冗长但语义明确
Parent.init(self)正确不推荐否硬编码类名,破坏协作链
super.init()错误——super 未实例化

换问法:若问“在钻石继承结构中,如何让所有父类的 init 都只执行一次?”,则答案应使用 super() 配合 MRO 链。

【自测】

补全以下代码,使 B 和 C 的初始化都能被 D正确调用:

python
class B:
    def __init__(self):
        print("B")
        super().__init__()
class C:
    def __init__(self):
        print("C")
        super().__init__()
class D(B, C):
    def __init__(self):
        print("D")
        ________  # 补全
D()

答:补全 super().__init__()。输出顺序为 D、B、C(按 MRO:D→B→C→object)。与第 P55 题连考。

【知识关联】

  • 同库关联:与 P55、J15(Java super)对照。
  • 实现层:super() 基于 MRO 不是简单父类;Py3 无参 super。
  • 面试追问:① 多继承中 super 调谁?② 与显式父类名?

【拓展延伸】

  • 变式问法:菱形继承中 super 链。
  • 版本差异:Py3 super() 更安全。
  • 工程注意点:初始化链务必 super().init;协作多重继承。

P57 · 知识点:多态与鸭子类型 ​

【题目】关于 Python 多态的说法,正确的是? A. 基于继承和“鸭子类型”,不强制要求显式接口,只要对象有对应方法即可调用 B. 必须实现接口才能实现多态 C. Python 不支持多态 D. 多态只适用于基本数据类型

答案:A

【考点】鸭子类型(duck typing)驱动的动态多态。

【结论】选 A。Python 的多态基于鸭子类型,只要对象实现了所需的方法或属性,就可以在不关心其具体类型的情况下使用。

【逐项辨析】

  • A. 基于继承和“鸭子类型”,不强制要求显式接口,只要对象有对应方法即可调用:正确。Python 的动态类型机制使得多态不需要统一的基类或接口声明,运行时根据对象实际能力决定行为。
  • B. 必须实现接口才能实现多态:错误。这是 Java、C# 等静态类型语言中接口多态的特征,Python 没有此强制要求。
  • C. Python 不支持多态:错误。Python 不仅支持多态,而且其多态机制比静态语言更灵活。
  • D. 多态只适用于基本数据类型:错误。多态在 Python 中广泛适用于所有自定义对象,例如 len() 可以对列表、字符串、字典、自定义对象等多种类型工作。

【知识点】多态(polymorphism)指同一接口在不同对象上产生不同行为。在 Java 中,多态通常通过继承父类或实现接口来实现编译时类型检查;而 Python 作为动态类型语言,采用鸭子类型(duck typing)——“如果它走起来像鸭子、叫起来像鸭子,那它就是鸭子”。这意味着只要对象具有所需的方法/属性,就可以在任何期望该能力的地方使用,无需显式声明继承关系或实现接口。例如内置函数 len(obj) 会调用 obj.len(),只要对象实现了 len 方法即可。这种动态多态极大地提高了代码的灵活性和可扩展性,但代价是将部分类型错误推迟到运行时。Python 也支持通过抽象基类(abc.ABC 和 @abstractmethod)来模拟静态接口约束,但这是一种可选的编码规范而非语言强制。

【记忆锚点】“鸭子类型不问出处,会叫会走就是鸭;Java 先看户口本,Python 只看本事大。”

【易混对比】

特性静态类型语言(Java/C#)Python
多态前提继承同一父类或实现接口具备所需方法/属性即可
类型检查时机编译期运行期
灵活性较低,需预先定义契约极高,任何对象只要“像”就行
错误暴露时机编译时报错运行时报错(若方法不存在)
实现方式interface/abstract classduck typing / ABC(可选)

换问法:若问“Python 中以下哪个函数体现了鸭子类型的设计?”,可答 len()、iter()、next() 等内置函数。

【自测】

以下代码能正常运行吗?输出什么?

python
class Dog:
    def speak(self): return "Woof"
class Cat:
    def speak(self): return "Meow"
def animal_sound(a):
    print(a.speak())
animal_sound(Dog())
animal_sound(Cat())

答:能正常运行,输出 "Woof" 和 "Meow"。animal_sound 不检查参数类型,只依赖 speak 方法的存在,是典型的鸭子类型多态。与第 P58 题连考。

【知识关联】

  • 同库关联:与 J13(Java 静态类型多态)对照——Python 鸭子类型。
  • 实现层:协议/魔术方法;运行期才失败(AttributeError)。
  • 面试追问:① 鸭子类型利弊?② 如何约束(typing.Protocol)?

【拓展延伸】

  • 变式问法:可迭代协议 iter;上下文管理器。
  • 版本差异:typing 体系完善。
  • 工程注意点:文档写清协议;静态检查靠 mypy Protocol。

P58 · 知识点:魔法方法 ​

【题目】让对象支持 len(obj) 调用,应实现?

A. len B. size C. str D. iter

答案:A

【考点】内置函数与魔法方法的映射关系。

【结论】选 A。要使自定义对象支持 len(obj) 调用,需要在类中实现 len(self) 方法并返回非负整数。

【逐项辨析】

  • A. len:正确。len(self) 是 len() 内置函数对应的魔法方法,返回值有两条硬约束,而且违反后抛的异常类型不同(本机 3.12.3 实测):返回负整数 → ValueError: __len__() should return >= 0;返回非整数(如 1.5)→ TypeError: 'float' object cannot be interpreted as an integer。所以「否则引发 ValueError」只对「负数」这一半成立。
  • B. size:错误。Python 中没有 size 这个标准魔法方法,len() 不会调用它。
  • C. str:错误。str 对应 str(obj) 和 print() 的调用,用于返回面向用户的字符串表示。
  • D. iter:错误。iter 对应 iter(obj) 和 for 循环的调用,用于返回迭代器对象,与长度计算无关。

【知识点】魔法方法(magic methods / dunder methods)是 Python 数据模型协议的核心,以双下划线开头和结尾,允许开发者自定义类以响应 Python 的内置语法和函数。常见映射关系:len() → len;str() → str;repr() → repr;iter() → iter;next() → next;obj[key] → getitem;obj[key] = value → setitem;with 语句 → enter / exit;运算符 + → add;运算符 == → eq;等等。实现 len 的对象被称为 Sized,是 collections.abc 中的一个抽象基类。注意 len 必须返回 int 且不能为负数,这是 Python 对序列/容器大小的硬性约束。

【记忆锚点】“len 找 len,str 找 str;iter 迭代器,size 没这回事。”

【易混对比】

内置调用魔法方法返回值类型触发场景
len(obj)lenint(≥0)获取元素个数
str(obj)strstrprint()、str()、format() 等
repr(obj)reprstr交互式环境、repr()
iter(obj)iteriteratorfor 循环、list() 等
bool(obj)boolbool条件判断、any()、all() 等
obj[k]getitem任意索引访问、切片

换问法:若问“让对象支持 print(obj) 输出自定义格式,应实现哪个魔法方法?”,则答案为 str(或 repr 作为兜底)。

【自测】

补全以下代码,使 len(b) 返回 3:

python
class Box:
    def __init__(self, items):
        self.items = items
    def ________(self):
        return len(self.items)
b = Box([1, 2, 3])
print(len(b))

答:补全 __len__。当调用 len(b) 时,解释器自动转换为 b.len()。与第 P59 题连考。

【知识关联】

  • 同库关联:与 P59(str/repr)、P53;双下划线方法。
  • 实现层:特殊方法由解释器在语法层调用;len→__len__ 等。
  • 面试追问:① 常用魔术方法有哪些?② __call__?

【拓展延伸】

  • 变式问法:运算符重载 add;容器协议。
  • 版本差异:持续新增(async 相关)。
  • 工程注意点:不要发明同名双下划线;dataclass 自动生成部分。

P59 · 知识点:str 与 repr ​

【题目】关于 str 和 repr 的区别,正确的是?

A. 两者没有区别 B. str 面向用户(str()、print() 调用),repr 面向开发者(repr()、交互式环境显示调用) C. print() 调用 repr D. str 必须返回整数

答案:B

【考点】字符串表示的两个层级。

【结论】选 B。str 提供面向终端用户的可读字符串,repr 提供面向开发者的无歧义、尽可能可重建对象的字符串表示。

【逐项辨析】

  • A. 两者没有区别:错误。两者设计目标完全不同,Python 在不同场景下优先调用不同方法。
  • B. str 面向用户(str()、print() 调用),repr 面向开发者(repr()、交互式环境显示调用):正确。str 追求可读性,repr 追求准确性和可重建性。
  • C. print() 调用 repr:错误。print(obj) 优先调用 str;仅在 str 未定义时才回退到 repr。
  • D. str 必须返回整数:错误。str 和 repr 都必须返回字符串(str 类型),返回其他类型会触发 TypeError。

【知识点】repr 的规范要求返回一个尽可能准确的字符串,理想情况下 eval(repr(obj)) == obj(虽然并非所有对象都能做到)。当在交互式解释器中直接输入对象名或调用 repr(obj) 时,Python 调用 repr。str 则用于 str(obj)、print()、format()、f-string 的 {!s} 转换等场景,追求对用户友好。如果类只定义了 repr 而没有定义 str,则 str 会回退使用 repr 的实现;反之不会回退。根据官方最佳实践:repr 应当始终被定义,作为开发的“安全网”;str 在有更友好的用户描述时才额外定义。例如 datetime 对象的 repr 返回 datetime.datetime(2024, 1, 1, 0, 0),而 str 返回 '2024-01-01 00:00:00'。

【记忆锚点】“repr 是调试内参,str 是门面展示;repr 求准不求美,str 求美不求细。”

【易混对比】

维度reprstr
目标受众开发者终端用户
追求目标无歧义、可重建可读性、友好性
触发场景repr()、交互式回显、%rstr()、print()、format()、%s
兜底行为无(最低层)若未定义则使用 repr
最佳实践必须实现按需实现更友好的版本
示例输出Point(1, 2)(1, 2)

换问法:若问“在 Python 交互式解释器中直接输入变量名显示的内容由哪个方法控制?”,则答案为 repr。

【自测】

以下代码在交互式环境中输入 p 和 print(p) 分别输出什么?

python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __repr__(self):
        return f"Point({self.x}, {self.y})"
    def __str__(self):
        return f"({self.x}, {self.y})"
p = Point(1, 2)

答:输入 p 输出 Point(1, 2)(调用 repr);print(p) 输出 (1, 2)(调用 str)。与第 P58 题连考。

【知识关联】

  • 同库关联:与 P58;str() 用 str,repr() 用 repr。
  • 实现层:调试用 repr,用户展示用 str;无 str 回落 repr。
  • 面试追问:① 二者用途?② 容器元素打印用哪个?

【拓展延伸】

  • 变式问法:print(obj) vs 交互式回显。
  • 版本差异:语义稳定。
  • 工程注意点:日志用 repr 更可定位;f-string 默认 str,{!r} 用 repr。

P60 · 知识点:类变量与实例变量 ​

【题目】输出结果是?

python
class A:
    x = 1
a1 = A()
a2 = A()
a1.x = 2
print(a2.x)

A. 1 B. 2 C. 抛异常 D. None

答案:A

【考点】类变量共享;实例赋值创建实例属性并遮蔽类变量。

【结论】选 A。a1.x = 2 创建的是 a1 的实例属性,不影响类变量,a2.x 仍读取类变量 A.x 的值为 1。

【推导过程】

  1. class A: x = 1 —— x 是类变量,存储在类对象 A 的 dict 中,所有实例共享。
  2. a1 = A(); a2 = A() —— a1 和 a2 被创建,此时两者都没有自己的实例属性 x,访问 a1.x 和 a2.x 都会通过属性查找链找到类变量 A.x,值为 1。
  3. a1.x = 2 —— 这是对实例 a1 的属性赋值,Python 会直接给 a1 的实例字典 dict 添加键值对 'x': 2,不会修改类变量 A.x。
  4. print(a2.x) —— a2 没有实例属性 x,继续向上查找类变量 A.x,仍为 1,因此输出 1。

【逐项辨析】

  • A. 1:正确。a1.x = 2 仅给 a1 创建了实例属性,a2 仍访问类变量 A.x = 1。
  • B. 2:错误。只有同时修改类变量(如 A.x = 2 或 a2.x = 2)才会让 a2.x 变为 2,a1.x = 2 不影响类变量。
  • C. 抛异常:错误。a2.x 可以正常找到类变量,不会引发 AttributeError。
  • D. None:错误。类变量 x 已初始化为 1,不会返回 None。

【知识点】Python 的属性查找遵循实例 → 类 → 父类的顺序。当通过实例访问属性时,先查实例 dict,再查类 dict;当通过实例赋值属性时,总是直接写入实例 dict(除非使用 slots 或描述符拦截)。类变量被所有实例共享,但如果某个实例对该名称进行了赋值操作,就会在该实例的 dict 中创建一个同名实例属性,从而“遮蔽”(shadow)类变量,使得后续通过该实例访问时优先看到实例属性而非类变量。修改类变量应通过类名赋值(A.x = 新值),这样所有未遮蔽的实例都会看到变化。可变类变量(如列表、字典)若通过实例方法修改其内容(如 append、update)会影响所有实例,但若重新赋值则会创建实例属性,这是常见的陷阱。

【记忆锚点】“实例赋值建新房,类变量还在老地方;要想大家一起变,A.x = 2 才有效。”

【易混对比】

操作实际效果影响范围常见陷阱
A.x = 2修改类变量所有未遮蔽的实例无
a1.x = 2创建实例属性仅 a1误以为修改了类变量
a1.x.append(3)(x 是列表)修改可变类变量内容所有实例共享可变对象的副作用
del a1.x删除实例属性仅 a1删除后 a1.x 又暴露类变量

换问法:若问“以下代码输出什么?class A: x = []; a1 = A(); a2 = A(); a1.x.append(1); print(a2.x)”,则答案为 [1],因为列表是类变量且被原地修改。

【自测】

以下代码的输出是什么?

python
class A:
    x = 1
a1 = A()
a2 = A()
A.x = 3
print(a1.x, a2.x)
a1.x = 5
A.x = 7
print(a1.x, a2.x)

答:第一行输出 3 3(修改类变量,所有实例可见);第二行输出 5 7(a1 已有实例属性 x,不受类变量修改影响;a2 仍访问类变量,变为 7)。与第 P54 题连考。

【知识关联】

  • 同库关联:与 P69(引用)、J19(Java static)对照——Python 类变量共享。
  • 实现层:类属性在类对象 __dict__;实例属性在实例 __dict__;赋值实例名遮蔽。
  • 面试追问:① 可变类变量共享陷阱?② 如何定义常量?

【拓展延伸】

  • 变式问法:列表类变量被所有实例共享并修改。
  • 版本差异:语义稳定。
  • 工程注意点:可变默认用实例属性在 init 创建;常量全大写模块级。

持续学习,持续积累。