您知道如何在 Python 中构建自己的类吗?在 Python 中编写自定义类和自定义对象可让您的代码更清晰、更易读且更易于维护。

在我们开始之前,如果你还需要一些学习 Python 的好理由,Rebecca 可以 在这里 .

面向对象编程的概念出现于 60 年代,但直到 90 年代才开始流行起来。如今,面向对象编程无处不在,并且是一种必须理解的编程范式。

面向对象编程是关于创建自定义对象的。对象是一组相互关联的函数和变量,它们相互作用。如果你不熟悉函数的概念,Kateryna 在这里 .

与面向过程编程相比,面向对象编程降低了代码复杂性,使其更清晰、更易于维护。它还可以通过封装来隐藏数据。面向过程编程缺乏这种安全性,因为所有函数都可以访问数据。面向对象编程可能有点困难,我建议您阅读我们的 Python 编程课程 .

在本文中,我将向您介绍 Python 中的自定义类是什么以及如何使用构造函数创建自定义类。然后,我将解释如何定义类属性和不同类型的方法。最后,在介绍 Python 自定义类的可见性之后,您将学习如何比较和对 Python 自定义对象执行操作。

使用构造函数在 Python 中创建自定义类

类是对象的集合。它是用户定义的数据结构,使用关键字 class 创建,用于将相关事物放在一起。因此,类是面向对象构造的分组。

我们来写一个简单的空类:

class Pokemon: 
	Pass

# instantiate the class Pokemon and assign it to a variable pokemon
pokemon = Pokemon()
print(pokemon)

输出:

<__main__.Pokemon object at 0x0000027B56ADD730>

因为我们的Python自定义类是空的,所以它只是返回对象存储的地址。

在面向对象编程中,自定义对象的属性由属性 (attribute) 定义,而其方法则定义其行为。方法有三种类型:

  • 实例方法
  • 类方法
  • 静态方法

在 Python 中, self 关键字表示类的实例。它充当访问类成员(例如来自类方法的属性)的句柄。它是该 __init__() 方法的第一个参数,并会自动调用以使用用户定义的值初始化类属性。

我们来运行一个例子:

class Pokemon:

    def __init__(self): 
        print("calling __init__() constructor...")

pokemon = Pokemon()

输出:

calling __init__() constructor...

但是,如果 Python 中的自定义类与功能无关,则毫无用处。功能是使用属性添加的,并充当数据的容器和这些属性的函数。这些函数称为方法。

Python 自定义类中的实例和类属性

让我们 Pokemon 使用 init() 创建属性的方法 name 更新 age 。这些属性称为实例属性。

class Pokemon:
    def __init__(self, name, attack):
        self.name = name 
        self.attack = attack

现在,让我们为我们的 Pokemon 类定义一个类属性:

class Pokemon:
    # Class attribute
    species = "Mouse"
    def __init__(self, name, attack): 
        self.name = name
        self.attack = attack

我们使用类属性来定义每个类实例具有相同值的属性,并使用实例属性来定义各个实例之间存在差异的属性。

让我们创造一些神奇宝贝。

class Pokemon:
    # Class attribute
    species = "Mouse"
    def __init__(self, name, attack): 
        self.name = name
        self.attack = attack
        
pikachu = Pokemon("Pikachu", "Double Kick")
raichu = Pokemon("Raichu", "Thunder Punch")

创建 Pokemon 实例后,我们可以使用点符号访问它们的实例属性, [instance name].[attribute name] 如下所示:

>>> pikachu.name
'Pikachu'
>>> pikachu.attack
'Double Kick'
>>> pikachu.species
'Mouse'
>>> raichu.name
'Raichu'
>>> raichu.attack
'Thunder Punch'

使用类来组织数据的主要好处之一是可以保证实例具有预期的属性。但是,这并不意味着我们不能动态地更改它们的值,例如:

>>> pikachu.attack = "Thunder Shock"
>>> pikachu.attack
'Thunder Shock'

Python 自定义类中的实例方法

实例方法是在类内部定义的函数,只能从该类的实例调用。例如 __init__() ,实例方法的第一个参数始终是 self .

让我们为 Python 自定义类定义一些实例方法 Pokemon .

class Pokemon:
    # Class attribute
    species = "Mouse"
    def __init__(self, name, attack): 
        self.name = name
        self.attack = attack

    # One instance method
    def description(self):
        return f"{self.name} favorite attack is {self.attack}"

    # A second instance method
    def speak(self, sound):
        return f"{self.name} says {sound}"

self 关键字必不可少。没有它,我们就无法访问 Python 中自定义类的属性和方法,从而导致错误。换句话说,它将属性与给定的参数绑定在一起。

让我们通过 Pokemon 动态创建一个新实例来使用我们的新实例方法:

>>> pichu = Pokemon("Pichu", "Nuzzle")
>>> pichu.description()
"Pichu favorite attack's is Nuzzle"
>>> pichu.speak("pichu pichu")
'Pichu says pichu pichu'

在上面的 Pokemon 类中, description() 实例 Pokemon 的信息的字符串 pichu 。当我们编写 Python 自定义类时,最好有一个方法返回一个包含有关该类实例的有用信息的字符串。

Python 自定义类中的类方法

类方法用于设置或获取类的状态。它们不能访问或修改特定的实例数据。方法用于描述对象的行为,并在类内部定义。

必须使用装饰器定义类方法 @classmethod 。它们还接受一个默认参数 cls ,该参数指向类。命名不是强制性的 cls ,但最好遵循惯例。

类方法用于创建工厂方法。工厂方法根据用例返回不同的类对象。

我们继续 Pokemon

class Pokemon:
    def __init__(self, names):
        self.names = names

    def __repr__(self):
        return f'Pokemon({self.names})'

    @classmethod
    def mouse(cls):
        return cls(['Pichu', 'Pikachu', 'Raichu'])

    @classmethod
    def hummingbird(cls):
        return cls(['Florabri', 'Floressum'])

调用构造函数,而是 Pokemon cls mouse 使用参数 hummingbird 。当我更改类名时,我不必在每个类方法中更新构造函数名称。

__repr__() 用于将类的对象表示为字符串。这意味着输出是对象的字符串表示形式。如果没有它,则输出为 Pokemon.mouse()

>>> Pokemon.mouse()
<__main__.Pokemon at 0x1d219dcb4f0>

这些类方法的作用如下:

>>> Pokemon.mouse()
Pokemon(['Pichu', 'Pikachu', 'Raichu'])
>>> Pokemon.hummingbird()
Pokemon(['Florabri', 'Floressum'])

因此,我们使用类方法来创建已经按照我们想要的方式配置的新 Pokemon 对象。

Python 自定义类中的静态方法

静态方法无法访问类数据,因为它们是自给自足的,可以独立工作。它们不附加到任何类属性,因此它们无法获取或设置实例状态或类状态。

要定义它们,我们需要使用 @staticmethod 装饰器。与实例和类方法不同,我们不需要传递任何默认参数。

静态函数用于创建执行常规编程任务的实用函数。让我们写一个例子,其中我们有一个静态方法来计算 Pokémon 攻击造成的伤害:

class Pokemon: 
    def __init__(self, power, level, names):
        self.power = power
        self.level = level
        self.names = names
        
    def __repr__(self):
        return (f'Pokemon({self.power}, '
                f'{self.level}, '
                f'{self.names})')
    
    def total_damage(self):
        return self.damage(self.power, self.level)

    @staticmethod
    def damage(power, level):
        return (power * level * 2) / 50

我修改了构造函数以接受 power level 参数并 __repr__() 显示它。我还添加了一个 total_damage() 实例方法,用于计算并返回 Pokémon 攻击时的伤害。并且,我没有直接在 中计算伤害等级,而是 total_damage() 静态方法 damage() 中计算伤害

让我们尝试一下:

>>> charmander = Pokemon(20, 8, "Charmander")
>>> charmander.total_damage()
6.4
>>> charmander.damage(20, 8)
6.4

这里的用例非常简单。静态方法无法访问 cls self 。它们的行为类似于常规函数,但属于类的命名空间,并且通常与对象生命周期无关。上述 damage() 方法完全独立于类,使测试更易于管理。

此外,在单元测试中测试方法之前,我们不必担心设置完整的类实例。我们像测试常规函数一样进行操作,使未来的维护更加容易。

为了强调这一点,让我们看看如果我们尝试在类本身上调用这些方法而不创建类的实例会发生什么:

class NewClass:
    def method(self):
        return 'Calling instance method...', self

    @classmethod
    def classmethod(cls):
        return 'Calling class method...', cls

    @staticmethod
    def staticmethod():
        return 'Calling static method...'
>>> NewClass.method()
TypeError: method() missing 1 required positional argument: 'self'
>>> NewClass.classmethod()
('Calling class method...', __main__.NewClass)
>>> NewClass.staticmethod()
'Calling static method...'

我们可以调用 classmethod() 和 staticmethod(),但尝试调用实例方法 method() 时失败,并出现 TypeError。这是因为我们尝试直接在类蓝图本身上调用实例函数,而没有创建该类的实例。Python 无法填充 self 参数,导致调用失败。相反,我们可以毫无问题地调用 staticmethod(),这证明该方法完全独立于类的其余部分。

Python 自定义类中的可见性

面向对象的编程语言(例如 C++ 和 Java)使用 public、private 和 protected 关键字控制对类的访问。与 C#、Java 和 C++ 等其他语言不同,Python 将 public、protected 和 private 访问修饰符概念化。

Python 中自定义类的公共成员

公共成员可以从类外部访问。这意味着我们可以自由地修改类属性,而不受任何限制。调用公共方法需要相同的类对象。这样做是为了遵循数据封装的原则。在 Python 中,类成员默认是公共的。

Python 中自定义类的受保护成员

类的受保护成员可从类内部访问,并且其子类也可以访问。

Python 没有任何机制来限制对任何实例变量或方法的访问。相反,它有一个惯例,即在变量或方法的名称前加上单下划线或双下划线,以模拟受保护和私有访问说明符的行为。为了保护实例变量, _ 会添加单下划线前缀(“ ”),从而阻止在子类中以外的人访问它。

请注意,这不会阻止实例变量访问或修改实例。

Python 中自定义类的私有成员

类的私有成员不能从类外部访问环境,只能在类内部处理。任何试图改变变量的行为都会导致 AttributeError .

的命名方式是在变量名称前 __ 添加双下划线前缀(“ _object._class__variable 。因此,它仍然可以从类外部访问,但应避免这种做法。

两种 Python 自定义对象的比较方法

在编码中,我们使用诸如 > 之类的运算符。但是,您需要使用 __gt__() 等运算符来实现 Python 自定义类功能。

以下方法用于比较对象的属性。为了帮助记忆,请查看注释中的大写字母。

class Value:
    def __init__(self, baz):
        self.baz = baz
    # Less Than operator
    def __lt__(self, obj2):
        return self.baz < obj2.baz
    # Greater Than operator
    def __gt__(self, obj2):
        return self.baz > obj2.baz
    # Less than or Equal operator
    def __le__(self, obj2):
        return self.baz <= obj2.baz
    # Greater than or Equal operator
    def __ge__(self, obj2):
        return self.baz >= obj2.baz
    # EQual operator
    def __eq__(self, obj2):
        return self.baz == obj2.baz
    # unequal (Not Equal) operator
    def __ne__(self, obj2):
        return self.baz != obj2.baz
foo = Value(6)
bar = Value(9)
print(
    foo < bar,
    foo > bar,
    foo <= bar,
    foo >= bar,
    foo == bar,
    foo != bar
)

输出:

True False True False False True

实例 foo 和 bar 包含一个名为 foo 的属性,该属性分别保存整数值 6 和 9。这是一个非常简单的示例。您的方法可以使用更高级的操作;例如,它们可以一次比较几个不同的属性。

两个 Python 自定义对象的数学运算

还可以对 Python 自定义对象执行数学运算。遵循上一个关于比较方法的代码片段的结构,下面是对两个 Python 自定义对象执行数学运算的代码片段:

class Value:

    def __init__(self, baz):
        self.baz = baz

    # Adding two objects 
    def __add__(self, obj2):
        return self.baz + obj2.baz

    # Subtracting two objects    
    def __sub__(self, obj2):
        return self.baz - obj2.baz

    # Multiplying two objects    
    def __mul__(self, obj2):
        return self.baz * obj2.baz

    # Dividing two objects    
    def __truediv__(self, obj2):
        return self.baz / obj2.baz

    # Get the remainder of a division of two objects    
    def __mod__(self, obj2):
        return self.baz % obj2.baz
        
foo = Value(2)
bar = Value(4)

print(
    foo + bar,
    foo - bar,
    foo * bar,
    foo / bar,
    foo % bar,
)

输出结果如下:

6 -2 8 0.5 2

此处的 文档 .

关于 Python 中自定义类的总结

在这篇关于创建 Python 自定义类和对象的介绍性文章中,我们已经介绍了很多内容。我选择不讨论析构函数这个话题,析构函数在对象被销毁时被调用。与 C++ 等其他编程语言不同,Python 有一个具有自动内存管理功能的垃圾收集器,因此析构函数的必要性就没那么大了。

这是一个复杂的话题,我们只是触及了皮毛。我强烈建议您通过使用上述代码片段并完成我们的 Python 编程轨迹 。您还可以查看 Dorota 的优秀 Python 学习资源列表 .

在下一篇文章中,我们将探讨 如何用 Python 编写模块 。与此同时,别忘了访问 !

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部