大家好,欢迎来到《基于 Python 的面向对象编程》系列的第 二 篇文章!在这个系列里,我们一起了解面向对象编程的基本概念,看看 Python 是怎么实现类的,还有怎么把这些知识用到实际开发中。希望大家一起轻松学习!
相关文章链接:
Python 是一种动态的、高级的编程语言,其设计哲学强调代码的可读性和简洁的语法。这些特性使得 Python 非常适合面向对象编程。Python 中的一切都可以被视为对象,从最基本的数据类型(如整数和字符串)到函数、类、甚至是模块。每个对象不仅包含数据(即属性),还包含绑定到这些数据上的操作(即方法)。这种设计极大地提升了语言的灵活性,允许开发者以统一的方式处理各种数据类型。
在 Python 中,类体的结构和基本语法是构建面向对象程序的核心。了解如何正确地定义和使用类是利用 Python 进行面向对象编程的关键。下面我们将详细地介绍类的定义、类体结构、基本语法及其组成部分。
类的声明
在 Python 中,使用关键字 class
来定义一个类。类名通常遵循大驼峰命名法(CapWords convention),即每个单词的首字母大写,不使用下划线。类定义的基本语法如下:
class ClassName:
# 类体定义
class
这个关键字标志着接下来的代码块将定义一个新的类。类定义以类名(如这里的 ClassName
)开始,类名后面紧跟一个冒号 “:
”,宣告了类体的开始。类体是一个缩进的代码块,包含了类的方法和属性定义。
Python 中类本身也是一个对象。每当我们使用 class
关键字定义新类时,Python 实际上是在创建一个对象。这个对象(类)自身具有创建新对象(实例)的能力,因此,类本质上是对象的“工厂”。这是 Python 中一切皆对象理念的一个重要体现。
运用 class
关键字定义新类时,Python 实际上是在创建一个对象。这也意味着类本身也是对象(实例)。那么这个新类是谁的对象呢?
该问题涉及到 Python 对象模型中比较深层次的概念。在 Python 中,类本身也是对象,这是通过元类(metaclass)实现的。元类是创建类的“类”,就像类是创建实例的模板一样。默认情况下,Python 中所有新定义的类都是 type
这个元类的实例,包括 Python 的内建类型如 int
、str
等。因此,当咱们定义一个新的类时,实质上是 type
类在创建一个新的对象。
class MyClass:
pass
这个定义等价于:
MyClass = type('MyClass', (), {})
这里,type
有三个参数,分别为:
- 类名(
MyClass
):新类的名称。 - 基类的元组(
()
):新类的基类集合,空元组意味着基类是object
。 - 字典(
{}
):包含类体定义的命名空间,比如方法和类变量。
这意味着 MyClass
是 type
的一个实例,就像 MyClass
的实例是 MyClass
类的对象一样。这构成了 Python 中一致的对象模型:所有东西都是对象,包括类本身。下面呈现了一个使用 type
动态创建类的示例:
# 动态创建一个类
MyDynamicClass = type('MyDynamicClass', (object,), {'x': 10, 'y': 20})
# 实例化这个类
instance = MyDynamicClass()
print(instance.x, instance.y) # 输出 10 20
这个等价于如下传统的类定义方式创建的 MyDynamicClass
类:
class MyDynamicClass:
x = 10 # 类属性
y = 20 # 类属性
instance = MyDynamicClass()
print(instance.x, instance.y) # 输出 10 20
虽然 type
是大多数类的默认元类,但 Python 也允许咱们定义自己的元类。这通常是通过继承 type
并重写 __new__
或 __init__
方法来完成的,使咱们可以在创建类时拦截和修改类的创建过程。比如:
class Meta(type):
def __new__(cls, name, bases, dct):
# 可以在这里修改类的定义
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=Meta):
pass
在这个示例中,MyClass
通过指定 metaclass=Meta
使用了自定义的元类 Meta
而不是默认的 type
。这允许 Meta
在 MyClass
被创建时修改其行为。
值得进一步说明的是,type
本身也是一个对象,在 Python 中,它是自己的一个实例。这意味着 type
不仅是创建其他类的工具,同时也描述了自己的结构。这种情况是一种特殊的自引用情形,可以理解为 type
是自己的元类:
type(type) is type # 返回 True
至于这种特殊的自引用特性到底是如何实现的,请查看和阅读 Cpython 的源代码。我也不懂😇。
类对象允许我们进行如下操作:
- 属性访问:访问类变量或通过类方法定义的计算属性。
- 函数调用:调用类方法。
- 实例化:通过调用类(如调用函数那样)来创建新的实例。
自定义的类在 Python 中的地位与内置类型如 int
和 str
相同。正如可以创建 int
或 str
的实例一样,我们也可以创建由自定义类定义的类型的实例。这种设计将自定义类型提升到与 Python 的内置类型同等的层次,使得使用自定义类型就像使用内置类型一样。
类体的定义
除了类的声明(class MyClass:
),类体是定义类的行为和状态的核心部分,它包含了数据属性(变量)和函数(方法)。
- 属性(Attributes)主要包括两类:
- 类变量:在类中定义,为类的所有实例所共享。用于存储与类相关的数据,而非单个实例相关的数据。
- 实例变量:通常在构造器(
__init__
方法)中通过self
参数定义。实例变量是每个类的实例独立拥有的数据。
- 方法(Methods)大致可以分为以下四类:
- 实例方法:定义操作实例数据的函数。第一个参数总是
self
,它代表类的一个实例本身。 - 类方法:使用
@classmethod
装饰器定义,其第一个参数是cls
,代表类本身。类方法可以访问类变量,但不能访问任何单独实例的数据。 - 静态方法:使用
@staticmethod
装饰器定义,不接受self
或cls
参数。静态方法不能访问类或实例的任何属性,主要用于放置不需要访问任何实例或类数据的工具性函数。 - 特殊方法(Magic Methods):如
__init__
(构造器)、__str__
(转换为字符串)、__repr__
(官方字符串表示)等。这些方法有特殊的名称和用途,是由 Python 的语法和行为规则预定义的。
- 实例方法:定义操作实例数据的函数。第一个参数总是
我们仍然以游戏角色为例,来进一步解释和理解类体的内容:
class Character:
level_up_experience = 100 # 类变量,所有实例共享,设定升级所需的经验值
def __init__(self, name, health, experience):
self.name = name # 实例变量,每个角色的名字
self.health = health # 实例变量,每个角色的健康点数
self.experience = experience # 实例变量,每个角色的经验值
def attack(self, other):
"""实例方法,角色可以攻击其他角色"""
if other.health > 0:
damage = 10
other.health -= damage
print(f"{self.name} attacks {other.name} causing {damage} damage points.")
self.gain_experience(10)
@classmethod
def change_level_up_experience(cls, new_experience):
"""类方法,允许修改升级所需的经验值"""
cls.level_up_experience = new_experience
print(f"Level up experience is now set to {new_experience}.")
@staticmethod
def game_info():
"""静态方法,提供游戏的一些基本信息,不依赖于类或实例的状态"""
return "This is a role-playing game."
def gain_experience(self, points):
"""实例方法,角色通过活动获得经验值"""
self.experience += points
print(f"{self.name} gains {points} experience points.")
if self.experience >= Character.level_up_experience:
self.level_up()
def level_up(self):
"""实例方法,角色升级"""
self.health += 50
print(f"{self.name} levels up! Health increased to {self.health}.")
def __str__(self):
"""特殊方法,定义角色的字符串表示形式"""
return f"Character({self.name}, Health: {self.health}, Experience: {self.experience})"
下图描述了 Charactor
类的组成:
Character
类的类体定义基本上包含了 Python 类体定义的常见组成部分,如类变量、实例变量、实例方法、类方法、静态方法和特殊方法。每个组成部分都扮演着特定的角色,共同定义了一个游戏中角色的行为和状态。
属性之类变量的定义
level_up_experience
在 Character
类中是类变量,用于定义所有角色升级所需的经验值。在 Python 中,类变量是定义在类的所有实例之间共享的变量。这些变量通常用于存储与类相关的数据,而非单个实例独有的数据。因为它们的行为与实例变量有显著不同,尤其是在涉及继承和方法访问这些变量时,因此,有必要进一步了解类变量的定义规则。
类变量应在类定义的顶层定义,即在任何方法之外。其赋值与在 Python 中定义全局变量或局部变量的方式相似,采用标准赋值语法,即使用赋值符号(=
)进行赋值。这样,类变量属于类的命名空间,而不是某个具体实例的命名空间。这与实例变量不同,实例变量在类的构造器(如 __init__
方法)中定义,使用 self
关键字并且每个实例维护自己的一份拷贝。修改一个实例的实例变量不会影响到其他实例。类变量的共享特性决定了类变量的值在一个实例中被修改时,这个修改对所有其他实例都将可见。这种行为使类变量适用于跨多个实例需要保持一致的数据。访问方式上,类变量可以通过类本身直接访问,也可以通过类的任何实例访问。然而,推荐使用直接访问的方式,即以类名称来访问类变量(如,Character.level_up_experience
),以区分类变量和实例变量,并避免在继承中可能出现的混淆。由于类变量在类的命名空间中,因此类变量可以在类中的任何方法(实例方法、类方法或静态方法)中使用。其实,在类方法中访问类变量尤为常见,因为类方法通常处理与类状态相关的操作。例如,在该示例中,类方法 change_level_up_experience
修改了类变量 level_up_experience
的值,达到更新升级所需要的经验值的目的。
属性之实例变量的定义
name
,health
以及 experience
在 Character
类中均是实例变量。name
变量用于定义角色的名字,为每个角色实例所独有。health
变量用于定义角色的健康点数,代表角色的当前健康状态。而 experience
用于定义角色的经验值,当经验值达到一定数值时,角色将升级。
如前所述,这些实例变量在类的构造器( __init__
方法)中初始化,它们为每个角色实例提供了独立的状态数据。注意,在构造器参数中有一个比较特殊的参数,即 self
关键字。self
在 Python 中代表当前对象的实例,是实例方法(包括构造器)的第一个参数。通过使用 self
,我们可以在类的其他方法中引用实例变量。比如说:
class Character:
def __init__(self, name, health, experience):
self.name = name # 实例变量,存储角色名
self.health = health # 实例变量,存储角色健康点数
self.experience = experience # 实例变量,存储角色经验值
def display_info(self):
# 方法中访问实例变量
print(f"Name: {self.name}, Health: {self.health}, Experience: {self.experience}")
这里的 display_info
方法中,我们通过 self.name
,self.health
,self.experience
分别访问了对应的实例变量。假设我们有一个 Character
类的实例 hero
:
hero = Character("Hero", 100, 50)
那么我们在使用 display_info
方法时(hero.display_info()
),将输出: Name: Hero, Health: 100, Experience: 50
。
在 Python 中,self
是一个约定俗成的名称,用于引用类的当前实例。从技术上讲,虽然咱们可以用其他任何名称替代 self
,但使用 self
是 Python 社区的标准做法,有助于维护代码的可读性和一致性,我们就不要修改了。另外,对于类的实例方法(包括构造器和析构器),self
在参数的中的位置也很重要,必须是方法的第一个参数。这不仅是一个强制性的约定,而且是 Python 解释器设计的要求。Python 解释器将方法调用转换为内部操作,自动将调用该方法的对象作为第一个参数传递。这一机制确保了方法能够访问其所属实例的属性和其他方法,从而操作和修改实例的状态。具体来说,当咱们写 obj.method(arg1, arg2)
时,Python 在内部实际上将其转换为 Class.method(obj, arg1, arg2)
,其中 obj
是 self
。这个转换是依赖于 Python 解释器自动进行,要求方法定义中的第一个参数必须接受这个对象引用。这种转换完全由 Python 解释器自动处理,并且强制要求方法定义中的首个参数接受这个对象引用。因此,当咱们在类中定义方法时,应确保将接收实例引用的参数(self
)置于参数列表的最前面。
除了传统的位置参数(name
, health
, experience
)和 self
这个比较特殊的参数外,由于类中的方法实质上就是函数,因此,我们可以在其中设置和使用不同类型的参数,包括关键字参数和默认参数及可变参数。其效果与普通函数中的参数效果一致。具体可以参考理解 Python 函数不同类型的参数及其使用。下面分别列举了使用默认参数、关键字参数以及可变参数的三个例子:
# 默认参数的使用意味着当创建类的实例时,可以不用提供所有参数,只提供那些需要与默认值不同的参数。
class Character:
def __init__(self, name, health=100, experience=0):
self.name = name
self.health = health
self.experience = experience
# 可以只提供名字,健康值和经验值将使用默认值
hero = Character("Hero")
# 关键字参数在构造器中的使用允许在不修改构造器定义的情况下传递额外的数据。这些数据可以用于动态设置实例属性或进行其他初始化任务。
class Character:
def __init__(self, name, **kwargs):
self.name = name
self.health = kwargs.get('health', 100)
self.experience = kwargs.get('experience', 0)
self.inventory = kwargs.get('inventory', [])
# 创建角色时可以传递任何额外的参数
hero = Character("Hero", health=150, experience=10, inventory=["sword", "shield"])
# 在不确定会传递多少参数,或者想要接受任意数量参数时可以使用可变参数。
class Character:
def __init__(self, name, *args):
self.name = name
self.abilities = list(args) # 将传入的能力转换为列表
# 创建一个角色,可以列举出任意多的能力
hero = Character("Hero", "strength", "agility", "wisdom")
在继承、封装与多态中,我们介绍过 Python 的类成员默认是公开(Public)的,但也提供了一些约定的方式实现实例变量和方法的访问控制。比如运用一个下划线(_
)前缀来表示变量或方法是受保护(Protected)的;双下划线(__
)前缀用来声明私有成员(Private)。这里,我们再举两个例子,以便加深印象。
class Character:
def __init__(self, name):
self._name = name # 受保护的实例变量
def _display_name(self):
# 受保护的方法
return f"This character is named {self._name}."
# 外部仍然能访问
hero = Character("Hero")
print(hero._name) # 打印出:Hero
print(hero._display_name()) # 打印出:This character is named Hero.
class Character:
def __init__(self, name):
self.__name = name # 私有实例变量
def __display_name(self):
# 私有方法
return f"This character is named {self.__name}."
# 外部尝试访问或修改会失败
hero = Character("Hero")
# print(hero.__name) # 报错,无法访问
# print(hero.__display_name()) # 报错,无法访问
print(hero._Character__name) # 打印出: Hero
print(hero._Character__display_name()) # 打印出:This character is named Hero.
可以看出,运用 _
来表示受保护的变量真的是一种约定,没有任何的约束,外部仍然可以轻松地直接访问。相对而言,使用 __
前缀来声明私有成员时,约束性稍微强了一些。我们不能直接访问该类的私有成员。如果,实在是有必要,我们只能通过在变量或者方法名前加上以 _
开头的类名的方式访问,如 hero._Character__name
和 hero._Character__display_name()
。
在 Python 中,名称前加单下划线(_
)或双下划线(__
)的机制主要用于类的属性和方法,以指示其访问级别。但是,这种机制也适用于模块级别的函数和变量,用以控制它们的可见性和访问性,尤其是在模块被导入时。然而,它们的含义和效果有所不同。
-
单下划线(
_
)开头: 在函数中,当函数名以单下划线(_
)开头时,它主要是作为一个内部使用的信号。虽然这不会影响函数的访问性(即它们仍然可以从模块外部被调用),但它是一个常见的约定,表明这个函数不应该被模块外的代码直接调用。在模块中,用于模块内部使用的函数或变量通常以单下划线开头。这是一个约定,向外部用户表明这些函数或变量是内部的,可能在未来版本中更改,不应在模块外部直接使用。 -
双下划线(
__
)开头: Python 中的普通函数如果以双下划线(__
)开头,它不会像在类属性和方法中那样触发名称改编(name mangling)。双下划线在普通函数中很少使用,因为它并不提供真正的访问保护。
以下示例展示了函数名和变量名前的下划线的使用:
# module.py
def _internal_function():
print("这是一个内部函数。")
def __name_mangling_not_applied():
print("这个函数的名称没有被改变。")
def public_function():
print("这是一个供外部使用的函数。")
_internal_function()
class MyClass:
def __init__(self):
self.__private_var = "这是私有的"
def __private_method(self):
print("这是一个私有方法")
def display(self):
print(self.__private_var)
self.__private_method()
# 使用实例
if __name__ == "__main__":
_internal_function() # 从模块内部调用,不推荐外部调用
__name_mangling_not_applied() # 没有应用名称改编
obj = MyClass()
obj.display()
如果想从外部其他文件导入相应的函数,都能正常工作,但不推荐,比如说:
# 其他文件尝试导入和使用:
from module import public_function, _internal_function, __name_mangling_not_applied
public_function() # 正常使用
_internal_function() # 正常使用,但不建议这么做,因为它违反了约定
__name_mangling_not_applied() # 正常使用,但真的没啥用,也不常见,所以也不推荐
方法之实例方法的定义
接着我们介绍类体中的实例方法的定义。下面这段代码呈现的是 Character
类的实例方法部分,以便查看。
class Character:
def attack(self, other):
"""实例方法,角色可以攻击其他角色"""
if other.health > 0:
damage = 10
other.health -= damage
print(f"{self.name} attacks {other.name} causing {damage} damage points.")
self.gain_experience(10)
def gain_experience(self, points):
"""实例方法,角色通过活动获得经验值"""
self.experience += points
print(f"{self.name} gains {points} experience points.")
if self.experience >= Character.level_up_experience:
self.level_up()
def level_up(self):
"""实例方法,角色升级"""
self.health += 50
print(f"{self.name} levels up! Health increased to {self.health}.")
在 Character
类中,attack()
,gain_experience()
,level_up()
均是实例方法。在 Python 中,实例方法本质上是定义在类中的函数,它们用于操作或者访问实例的属性(即对象的状态)。实例方法在定义时,必须至少包括一个参数,通常是 self
,它代表类的实例本身。此外,实例方法可以接收额外的参数,参数类型可以是位置参数、关键字参数以及可变参数。这些参数在方法被调用时传递。例如, attack()
方法中,除了self
这个必须的参数外,还接受了 other
这个表示另一个角色实例的参数。attack()
方法允许一个角色实例(self
)攻击另一个角色实例(other
)。该方法首先检查被攻击者的健康值(health
)是否大于 0,如果是,则执行攻击,减少被攻击者的健康值,并增加攻击者的经验值(通过调用 gain_experience()
方法)。期间,该方法使用 print()
函数打印了攻击动作的详情,包括攻击者和被攻击者的名字,以及造成的伤害点数。
方法体中可以通过 self.attribute_name
的方式访问或修改实例的属性,如 attack()
方法中的 self.name
访问了 Character
类的一个实例的角色名称;或者通过 self.method_name()
的方式调用同一个类的其他方法,如 gain_experience()
方法中,self.level_up()
调用了 level_up()
方法,以此实现增加角色的健康值(health
),并给出新的健康值的目的。
值得说明的是,该示例中的实例方法展示了一种常规的定义方式。其实,Python 允许实例方法访问和调用类的各种组件,这包括访问和修改类变量,调用类方法和静态方法,以及访问私有和受保护的属性和方法。这使得 Python 的面向对象编程时非常灵活。
方法之类方法的定义
类方法在 Python 中是通过在方法定义前使用 @classmethod
装饰器来标识的。这种方法不是针对类的实例,而是针对类本身进行操作。它们常用于需要访问或修改类级别属性(即类变量)的场景。比如 Character
类中的 change_level_up_experience()
方法:
class Character:
level_up_experience = 100 # 类变量,所有实例共享,设定升级所需的经验值
@classmethod
def change_level_up_experience(cls, new_experience):
"""类方法,允许修改升级所需的经验值"""
cls.level_up_experience = new_experience
print(f"Level up experience is now set to {new_experience}.")
从该类方法的定义方式上看,我们可以注意到如下几个关键元素:第一,@classmethod
装饰器。这个装饰器是必需的,它告诉 Python 这个方法是一个类方法。第二,第一个参数 cls
。类方法的第一个参数通常命名为 cls
,这代表了类本身。这个参数由 Python 在调用类方法时自动提供,类似于实例方法中的 self
参数,但 cls
指向的是类而不是实例。
关于装饰器,请查看“装饰器”这一小节的介绍。
此外,类方法的方法体中也可以通过 cls.class_variable_name
或者 cls.class_method()
的访问和修改类变量或者调用其他的类方法。我们来看一下下面这个示例:
class Character:
total_characters = 0 # 类变量,记录游戏中角色的总数
level_up_experience = 100 # 类变量,设定升级所需的经验值
def __init__(self, name, experience=0):
self.name = name
self.experience = experience
self.level = 1
Character.total_characters += 1 # 每创建一个角色,总数加一
@classmethod
def change_level_up_experience(cls, new_experience):
"""类方法,修改所有角色的升级经验阈值"""
cls.level_up_experience = new_experience
print(f"Updated level up experience to {new_experience} for all characters.")
@classmethod
def reset_game(cls):
"""类方法,重置游戏状态,包括角色总数和默认经验值"""
cls.total_characters = 0
cls.level_up_experience = 100
print("Game has been reset. All character counts and experiences are set to default.")
@classmethod
def check_and_reset_game(cls):
"""检查是否需要重置游戏,并调用重置"""
if cls.total_characters > 10: # 假设超过10个角色就重置游戏
cls.reset_game()
def gain_experience(self, points):
"""实例方法,增加经验并判断是否升级"""
self.experience += points
if self.experience >= Character.level_up_experience:
self.level_up()
def level_up(self):
"""实例方法,处理角色升级逻辑"""
self.level += 1
print(f"{self.name} has leveled up to level {self.level}!")
# 使用示例
for i in range(12): # 创建多个角色
char = Character(f"Hero{i}")
Character.check_and_reset_game() # 检查并可能重置游戏
在这个例子中,我们增加了一个表示总的角色数量的类变量(total_characters
),以及可以重置游戏状态的类方法(reset_game()
)和检查是否需要重置游戏的类方法(check_and_reset_game()
)。在 change_level_up_experience
类方法中,我们使用 cls.level_up_experience
的方式来访问类变量 level_up_experience
。通过 level_up_experience = new_experience
来更新升级所需的经验值。在 reset_game
中,我们通过 cls.total_characters = 0
以及 cls.level_up_experience = 100
的方式重置了总的角色数量和升级所需的经验值。在 check_and_reset_game()
类方法中,我们首先检查了是否需要重置游戏的标准(假设超过 10 个角色就重置游戏),如果满足条件,就通过 cls.reset_game()
方式调用类方法 reset_game()
来重置游戏,将总的角色数量和升级所需的经验值均设定为初始值。
方法之静态方法的定义
在 Python 中,静态方法通过 @staticmethod
装饰器来定义,后面跟着一个普通的函数定义。注意,静态方法既不需要类 (cls
) 也不需要实例 (self
) 的引用来调用,它们就像是类中的普通函数。静态方法的主要用途是在它们的方法体内执行一些功能,而这些功能与类的具体实例或者类本身的状态无关。这使得静态方法成为实现与类功能相关但独立于类实例的方法的理想选择。比如 Character
类中的 game_info()
方法:
class Character:
@staticmethod
def game_info():
"""静态方法,提供游戏的一些基本信息,不依赖于类或实例的状态"""
return "This is a role-playing game."
在使用场景上,静态方法通常用于以下情景:
- 用于大型类中,为类提供相关功能,但又不需要类或实例的状态(即,属性或方法)时。
- 作为工具函数,用于执行某些任务,比如格式化数据,处理输入等,这些不直接与类的任何实例的内部状态相关的操作。
相较于其他方法,静态方法还有一个明显的特征。在使用静态方法时,我们并不需要实例化对象。比如说在 Character
类这个例子中,我们可以通过 print(Character.game_info())
直接打印出该游戏的信息(Output:This is a role-playing game.
),而无需创建 Character
类实例。这种机制直接的好处是,可以节省内存的使用。
为了加深印象,我们可以再看一个例子:
class MathUtility:
@staticmethod
def add_numbers(a, b):
return a + b
@staticmethod
def multiply_numbers(a, b):
return a * b
MathUtility
类中定义了两个静态方法:add_numbers()
和 multiply_numbers()
。我们可以按照如下方式使用这两个静态方法:
# 使用静态方法
result_add = MathUtility.add_numbers(10, 5) # 无需创建类实例
result_multiply = MathUtility.multiply_numbers(10, 5)
print("Addition:", result_add) # 输出 15
print("Multiplication:", result_multiply) # 输出 50
如果 add_numbers()
和 multiply_numbers()
为普通的实例方法:
class MathUtility:
def __init__(self, a, b):
self.a = a
self.b = b
def add_numbers(self):
return self.a + self.b
def multiply_numbers(self):
return self.a * self.b
那么,我们要使用这两个方法时,只能先对 MathUtility
类进行实例化(Instantiate),然后调用这两方法:
# 创建 MathUtility 类的实例
math_util = MathUtility(10, 5)
# 使用实例方法
result_add = math_util.add_numbers() # 调用实例方法
result_multiply = math_util.multiply_numbers()
print("Addition:", result_add) # 输出 15
print("Multiplication:", result_multiply) # 输出 50
在此情景下,如果仍然按照静态方法的使用方式,比如:
result_add = MathUtility.add_numbers(10, 5) # 没有创建类实例
通常会抛出以下错误:
TypeError: MathUtility.add_numbers() takes 1 positional argument but 2 were given