2022年 11月 8日

Python中类的介绍及使用

文章目录

  • 前言
  • 一、面向对象与面向过程
  • 二、类的创建和使用
  • 三、使用类和实例
    • 1.给属性指定默认值
    • 2.修改属性的值
  • 四、继承
    • 1.子类的方法__init__()
    • 2.给子类定义属性和方法
    • 3.重写父类的方法
    • 4.将实例用作属性
  • 五、类的导入
  • 六、Python标准库
  • 总结

前言

本文的主要内容是Python中类的介绍及使用,具体包括面向对象与面向过程的介绍、类的创建和使用、使用类和实例、继承、类的导入和Python标准库的使用,文中附有代码以及相应的运行结果辅助理解。


一、面向对象与面向过程

面向过程(procedure-oriented):注重过程,在解决一个问题的时候,面向过程会把事情拆分成 一个个函数和数据,然后按照一定的顺序,依次执行这些方法,等方法执行完了,问题就解决了。
面向对象(object-oriented):注重对象,在解决一个问题的时候,面向对象会把事物抽象成若干个对象,然后再给对象赋一些属性和方法,让每个对象去执行自己的方法,问题得到解决。
举一个例子:洗衣机洗衣服。
面向过程:
1.把衣服放进洗衣机;
2.往洗衣机中注水;
3.加入洗衣液;
4.启动洗衣机;
5.洗衣机清洗;
6.洗衣机烘干。
面向过程洗衣服就是按照顺序按部就班的执行一个个的“函数”。
面向对象:
先抽象出两个对象:“人”和“洗衣机”。
对“人”加一些属性和方法:“把衣服放进洗衣机”、“往洗衣机中注水”、“加入洗衣液”、“启动洗衣机”。
对“洗衣机”加一些属性和方法:“洗衣机清洗”、“洗衣机烘干”。
执行:人——“把衣服放进洗衣机”;人——“往洗衣机中注水”;人——“加入洗衣液”;人——“启动洗衣机”;洗衣机——洗衣机清洗;洗衣机——洗衣机烘干。
面向对象洗衣服就是先抽象出类,然后各个类再分别调用“方法”。


二、类的创建和使用

面向对象编程是最有效的软件编写方法之一,在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。编写类时,你定义一大类对象都有的通用行为,基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。根据类来创建对象被称为实例化,这让你能够使用类的实例。
根据约定,在Python中,首字母大写的名称指的是类,类中的函数称为方法,通过实例访问的变量称为属性。
方法__init__()是一个特殊的方法,每当你给类创建新的实例时,Python都会自动运行它。在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。在这个方法的定义中,形参self是必不可少的,还必须位于其他形参的前面,因为Python调用__init__()方法来创建实例时,将自动传入实参self,因此我们不需要传递它,只需要给self后面的参数传递实参即可。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法。
下面是一个类的创建和使用的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        #获取存储在形参first_name和last_name中的值,并将其存储到变量name中
        self.gender = gender
        self.age = age
        #这里的name,gender和age都是属性

    def describe_user(self):
        """描述用户的方法"""
        if self.gender == 'male':
            print("This user's name is " + self.name + ", and his age is " + str(self.age) + ".")
        elif self.gender == 'female':
            print("This user's name is " + self.name + ", and her age is " + str(self.age) + ".")

    def greet_user(self):
        """问候用户的方法"""
        print("Hello, " + self.name + "!")
    #这里的describe_user()和greet_user()都是方法

user_1 = User('lebron', 'james', 'male', 37) #根据类创建实例,Python遇到这行代码会调用方法__init__()来提供属性

print('name : ' + user_1.name) #用句点表示法访问实例的属性
print('gender : ' + user_1.gender)
print('age : ' + str(user_1.age))

user_1.describe_user() #用句点表示法调用方法
user_1.greet_user()

print()

user_2 = User('rachel', 'green', 'female', 24) #创建多个实例

print('name : ' + user_2.name) #用句点表示法访问实例的属性
print('gender : ' + user_2.gender)
print('age : ' + str(user_2.age))

user_2.describe_user()
user_2.greet_user()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

运行结果如下图所示。
在这里插入图片描述
由运行结果可知,对于类中的属性可以用句点表示法访问实例的属性,也可以直接调用类中的方法。


三、使用类和实例

可以使用类来模拟现实世界中的很多情景,类编写好以后,大部分时间都将花在使用根据类创建的实例上,还要执行的一个重要任务是修改实例的属性,可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。

1.给属性指定默认值

类中的每个属性都必须有初始值,即使这个值是0或空字符串。在有些情况下,如设置默认值时,如果在方法__init__()内指定了初始值,那么在后面的括号中就无需包含其形参。
下面是给属性指定默认值的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age): #不用给login_attempts传递形参
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        #获取存储在形参first_name和last_name中的值,并将其存储到变量name中
        self.gender = gender
        self.age = age
        self.login_attempts = 0  #给属性指定默认值后不用再传递形参

    def print_login_attempts(self):
        """打印属性login_attempts"""
        print('login_attempts in function: ' + str(self.login_attempts))

user = User('lebron', 'james', 'male', 37) #不用给login_attempts传递实参

print('name : ' + user.name) #用句点表示法访问实例的属性
print('gender : ' + user.gender)
print('age : ' + str(user.age))
print('login_attempts : ' + str(user.login_attempts)) #直接访问实例属性

user.print_login_attempts() #调用方法打印属性login_attempts
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

运行结果如下图所示。
在这里插入图片描述
由运行结果可知,我们给属性指定默认值之后,可以直接访问实例的该默认属性,也可以通过方法来访问,其结果都是一样的。

2.修改属性的值

修改属性的值可以用三种不同的方式:直接通过实例进行修改;通过方法进行修改;通过方法进行递增。
下面是修改属性值的例子,里面包含了上面提到的三种方法。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age): #不用给login_attempts传递形参
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        #获取存储在形参first_name和last_name中的值,并将其存储到变量name中
        self.gender = gender
        self.age = age
        self.login_attempts = 0  #给属性指定默认值后不用再传递形参

    def print_login_attempts(self):
        """打印属性login_attempts"""
        print('print_login_attempts login_attempts : ' + str(self.login_attempts))

    def update_login_attempts(self, value):
        """修改属性login_attempts的值"""
        self.login_attempts = value
        print('update_login_attempts login_attempts : ' + str(self.login_attempts))

    def increment_login_attempts(self, value):
        """修改属性login_attempts的值"""
        self.login_attempts += value
        print('increment_login_attempts login_attempts : ' + str(self.login_attempts))

user = User('lebron', 'james', 'male', 37) #不用给login_attempts传递实参

print('name : ' + user.name) #用句点表示法访问实例的属性
print('gender : ' + user.gender)
print('age : ' + str(user.age))
print('original login_attempts : ' + str(user.login_attempts)) #直接访问实例属性

user.login_attempts = 10  #直接通过实例进行修改默认属性值
user.print_login_attempts() #调用方法打印属性login_attempts

user.update_login_attempts(20) #调用方法修改默认属性的值

user.increment_login_attempts(10) #调用方法对默认属性值递增
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

运行结果如下图所示。
在这里插入图片描述
由上面运行结果可知,原始的login_attempts值为0;直接通过实例进行修改其值后为10;通过方法修改其值时会覆盖掉原来的10,修改为20;再调用方法对属性值递增,递增时不会覆盖掉已有的login_attempts值20,而是在其基础上递增10,结果即为30。


四、继承

并非所有类的编写总是从空白开始,如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法,原有的类称为父类(超类),而新类称为子类,子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。
创建子类时,父类必须包含在当前文件中,且位于子类前面。
定义子类时,必须在括号内指定父类的名称。

1.子类的方法__init__()

创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值,而子类的方法__init__()需要父类施以援手。
super()是一个特殊函数,帮助Python将父类和子类关联起来。
下面是子类的方法__init__()编写的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        self.gender = gender
        self.age = age

    def describe_user(self):
        """描述用户的方法"""
        if self.gender == 'male':
            print("This user's name is " + self.name + ", and his age is " + str(self.age) + ".")
        elif self.gender == 'female':
            print("This user's name is " + self.name + ", and her age is " + str(self.age) + ".")

    def greet_user(self):
        """问候用户的方法"""
        print("Hello, " + self.name + "!")


class Administrator(User):
    """定义子类Administrator继承父类User"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化父类的属性"""
        super().__init__(first_name, last_name, gender, age)


admin = Administrator('lebron', 'james', 'male', 37)
admin.greet_user()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

运行结果如下图所示。
在这里插入图片描述
通过上面的例子可以看到,子类Administrator继承了父类User的属性和方法,在子类的__init__()方法中需要用到super()来初始化父类的属性。子类继承父类后,就可以用父类中的属性和方法了。

2.给子类定义属性和方法

让一个类继承另一个类后,可添加区分子类和父类所需的新属性和方法。
下面是给子类定义属性和方法的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        self.gender = gender
        self.age = age

    def describe_user(self):
        """描述用户的方法"""
        if self.gender == 'male':
            print("This user's name is " + self.name + ", and his age is " + str(self.age) + ".")
        elif self.gender == 'female':
            print("This user's name is " + self.name + ", and her age is " + str(self.age) + ".")

    def greet_user(self):
        """问候用户的方法"""
        print("Hello, " + self.name + "!")


class Administrator(User):
    """定义子类Administrator继承父类User"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化父类的属性"""
        super().__init__(first_name, last_name, gender, age)
        self.identity = 'administrator'  # 添加子类的特有属性

    def greet_admin(self):   # 添加子类的特有方法
        """问候管理员的方法"""
        print("Dear " + self.identity + ":")
        print("Welcome to login, " + self.name + "!")


admin = Administrator('lebron', 'james', 'male', 37)
admin.greet_user() #调用子类继承父类的方法
admin.greet_admin() # 调用子类特有的方法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

运行结果如下图所示。
在这里插入图片描述
可以看到,子类除了有父类的属性和方法,还可以添加新的属性和方法。

3.重写父类的方法

对于父类的方法,只要它不符合子类的要求,都可对其进行重写。为此,可在子类中定义一个这样的方法,即它与要重写的父类方法同名。这样,Python将不会考虑这个父类方法,而只关注你在子类中定义的相应方法。
下面是重写父类方法的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()
        self.gender = gender
        self.age = age

    def describe_user(self):
        """描述用户的方法"""
        if self.gender == 'male':
            print("This user's name is " + self.name + ", and his age is " + str(self.age) + ".")
        elif self.gender == 'female':
            print("This user's name is " + self.name + ", and her age is " + str(self.age) + ".")

    def greet_user(self):
        """问候用户的方法"""
        print("Hello, " + self.name + "!")


class Administrator(User):
    """定义子类Administrator继承父类User"""
    def __init__(self, first_name, last_name, gender, age):
        """初始化父类的属性"""
        super().__init__(first_name, last_name, gender, age)
        self.identity = 'administrator'  # 添加子类的特有属性

    def greet_admin(self):   # 添加子类的特有方法
        """问候管理员的方法"""
        print("Dear " + self.identity + ":")
        print("Welcome to login, " + self.name + "!")

    def describe_user(self):
        """重写父类的方法describe_user()"""
        if self.gender == 'male':
            print("This admin's name is " + self.name + ", and his age is " + str(self.age) + ".")
        elif self.gender == 'female':
            print("This admin's name is " + self.name + ", and her age is " + str(self.age) + ".")

user = User('lebron', 'james', 'male', 37)
user.describe_user()  # 调用父类方法

admin = Administrator('emma', 'green', 'female', 24)
admin.describe_user()  # 调用子类重写的父类方法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

运行结果如下图所示。
在这里插入图片描述
通过上面运行结果的对比就可以知道,子类成功重写了父类的方法。

4.将实例用作属性

使用代码模拟实物时,你可能会发现给类添加的细节越来越多,属性和方法清单以及文件都越来越长,在这种情况下,可能需要将类的一部分作为一个独立的类提取出来,将大型类拆分成多个协同工作的小类。
下面是将实例用作属性的例子。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()

    def print_name(self):
        """打印姓名"""
        print("name : " + self.name)


class Attribute():
    """定义一个属性类"""
    def __init__(self, gender, age):
        """初始化属性"""
        self.gender = gender
        self.age = age

    def describe_attribute(self):
        """描述属性的方法"""
        print("gender : " + self.gender)
        print("age : " + str(self.age))


class Administrator(User):
    """定义子类Administrator继承父类User"""
    def __init__(self, first_name, last_name):
        """初始化父类的属性"""
        super().__init__(first_name, last_name)
        self.attribute = Attribute('female', 24)  # 将类用作属性


admin = Administrator('emma', 'green')
admin.print_name()
admin.attribute.describe_attribute()  # 调用属性中的方法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

运行结果如下图所示。
在这里插入图片描述
通过上面的例子可知,一个类可以作为另一个类的属性,这样一来,我们在完成一个复杂的问题时就可以将其拆解为多个小问题,每个小问题对应着一个小类,最后将它们合在一个大类中,大的问题就比较容易的解决了。


五、类的导入

如果不断地给类添加新的功能,文件可能变得很长,即便妥善地使用了继承也是如此,因此,可以将类存储在模块中,然后在主程序中导入所需的模块。
可以在一个模块里只存储一个类,比如在模块user.py中存储着名为User的这一个类,在主程序代码模块中导入该类时使用如下代码。

from user import User
  • 1

也可以在一个模块中存储多个类,比如在模块user.py中存储着名为User、Admin等多个类,在主程序代码模块中要导入其中的几个类时使用如下代码。

from user import User, Admin
  • 1

还可以使用如下代码导入user.py整个模块,再使用句点表示法访问需要的类。

import user
  • 1

导入user.py模块中的所有类使用如下代码。

from user import *
  • 1

下面是导入类的例子。
模块user.py中的代码如下。

class User():
    """定义用户的类"""
    def __init__(self, first_name, last_name):
        """初始化属性"""
        self.name = first_name.title() + ' ' + last_name.title()

    def print_name(self):
        """打印姓名"""
        print("name : " + self.name)


class Attribute():
    """定义一个属性类"""
    def __init__(self, gender, age):
        """初始化属性"""
        self.gender = gender
        self.age = age

    def describe_attribute(self):
        """描述属性的方法"""
        print("gender : " + self.gender)
        print("age : " + str(self.age))


class Administrator(User):
    """定义子类Administrator继承父类User"""
    def __init__(self, first_name, last_name):
        """初始化父类的属性"""
        super().__init__(first_name, last_name)
        self.attribute = Attribute('female', 24)  # 将类用作属性
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

主程序中的代码如下。
1.导入user.py整个模块。

import user

admin = user.Administrator('emma', 'green')
admin.print_name()
admin.attribute.describe_attribute()  # 调用属性中的方法
  • 1
  • 2
  • 3
  • 4
  • 5

2.从模块user.py中导入类。

from user import Administrator
# from user import *
# from user import User, Attribute, Administrator
# 对于本例,以上三条导入语法的运行结果一致

admin = Administrator('emma', 'green')
admin.print_name()
admin.attribute.describe_attribute()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

对于本例而言,上面主程序中的代码不管是导入user.py整个模块还是从模块user.py中导入类,其运行结果都一样,如下图所示。
在这里插入图片描述
合理的导入类对于代码的可读性和维护性都有很大的好处。


六、Python标准库

Python标准库是一组模块,安装的Python都包含它,这是其他程序员编写好的模块,有时候我们需要用到它们,在程序开头包含一条import语句即可导入这些模块。
下面是Python标准库应用的例子。

from random import randint


class Dice():
    """掷骰子"""
    def __init__(self):
        self.sides = 6  #默认是6面的骰子

    def roll_dice(self):
        """打印骰子点数"""
        print(randint(1, self.sides))

    def change_side(self,side):
        """改变骰子面数"""
        self.sides = side


dice = Dice()
dice.roll_dice()  # 点数在1-6之间

dice.change_side(10)
dice.roll_dice()  # 点数在1-10之间

dice.change_side(20)
dice.roll_dice()  # 点数在1-20之间
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行结果如下图所示。
在这里插入图片描述
本例用到的是random模块中的randint函数,这是Python自带的库,不用我们自己写,使用起来非常方便,使用Python标准库可以极大的方便我们编程。


总结

以上就是Python中类的介绍及使用的所有内容了,类的创建和使用是最基础也是最重要的;在继承中,子类既保留了父类的属性方法,还可以自己创建独有的属性和方法,将实例用作属性也是非常高效的编程手段;类的导入和Python标准库都是为了方便我们更好的编程,应当熟练运用。
本文参考书目:Python 编程 :从入门到实践 / (美) 埃里克•马瑟斯(Eric Matthes)著;袁国忠译
参考博文:
面向对象与面向过程的本质的区别
https://zhuanlan.zhihu.com/p/75265007