6. 类详解

一、前言

类同样是编程语言中非常常见的一个概念,如果你想要对类有更加宏观的认识,可以参考本站的另一篇文章:结构体、类、接口

为了照顾新手,我们还是来简单说一下。

从前面的代码中我们可以发现,写代码无非就是写两个内容:变量、函数。

  • 变量:用来存放数据
  • 函数:一系列代码语句的集合,一般用来封装一系列常用的代码语句。

显然,这两部分是分开的,很多时候这种分离是合理的,比如前面的提到的len函数,它就可以求许多类型的变量长度。

但有时候分开却并不太好,比如一个数字,我想要写一个函数对这个数字做一个非常复杂的运算,比如:

def js(n: int):
    # 假设这里对数字做了大量复杂运算
    return n

那么其实这个函数是只能给数字用的,其它类型的变量你就算想用也用不了!

这时候问题就来了:

  • 首先就是这个函数名必须得放在全局,让其它想使用的地方随时都能使用不是?就像len函数一样。

然而这只是一个函数,实际开发中怎么都得有几十上百个函数,先不说这些函数会不会有重名的风险,只是让你记函数名都很困难吧!

  • 其次,这只是对于一个int类型变量,可能会很简单,但实际开发过程中,一般都是一系列基本数据类型组成的一个新类型,填写参数就会很麻烦不是吗?

比如一个学生信息,就得有学号、姓名、性别等等很多变量,如果你想要处理这个学生信息,可能就得每次都挨个将其传入函数中,是不是非常的麻烦?

这样的麻烦事有很多,而这些麻烦事都可以用类这个概念来解决!

类的基本概念就是:将所有相关的数据、函数都包装成一个抽象的集合

比如上面提到的一个学生,他可能有很多身份数据信息,那就把这些身份数据信息全都放在一个学生类中就可以了,这样一个新的自定义数据类型就产生了:学生

对于学生而言,它可能有许多相关的操作,比如:设置姓名、年龄、学号、并按照一定的格式标准输出这些相关信息。

那这些操作就可以对应一个又一个函数,并且将其绑定到(封装到)这个类中,那么之后这个函数就只有属于这个类的对象可以调用,其他类型是无法调用的(因为绑定了)。

而且由于封装在了类中,你就不再需要记住这些函数名,因为IDE可以给我们相关的提示信息,你只需要大概记得有这么个函数、知道它的前两个字母就行了。

二、简单使用

上面是理解,但只是空说其实也不太好理解,所以这里先试着封装一个简单的类:

class Student:
    def set_name(self, name: str):
        self.name = name

    def set_age(self, age: int):
        self.age = age
    def fmt(self):
        return self.name+" "+str(self.age)

s=Student()
s.set_age(100)
s.set_name('yushi')
print(s.fmt())

上面的代码并不很难,缩进什么的应该不用我再多说什么了,简而言之就是只要你这段代码属于外面一层,那就需要一个tab键。

要使用一个类,首先需要声明,使用关键字class,其后跟着你想要的类名称,比如我这里的是Student

然后你就可以在里面定义一系列函数了!

是不是觉得有点奇怪?明明上面我说的是类封装函数变量,怎么这里只有函数呢?

因为类是一个很抽象的概念,并没有分配具体的内存,加上python是一个弱类型的语言,所以它的变量是可以动态绑定的。

也就是set_name等函数中,第一个参数self,就指代当前这个类型,然后只要我给它赋值一个变量,那它就会自动生成这么一个变量,用的方式就是点.

类中函数第一个参数就是用来指代这个对象本身的,一般用的是单词:self

但其实你使用其它单词也是可以的

使用方法就很简单了,就和前面调用函数差不多:直接用Student()就能返回一个这个类型的变量。

由于这是一个类,它内部是绑定了函数的,所以你就可以直接用点的方式使用这些函数:

image-20240309124110159

这个时候,你就可以看到IDE的提示信息了,蓝色图标代指这是一个变量,紫色图标代指这是一个函数。

其中还有大量的__*__的变量、函数,这是python为每个类自动生成的东西,也可以调用(具体来说涉及到了类的继承,所有类都默认继承自Object,这些多出来的函数、变量也是这个类身上的)。

所以下面两句代码其实是等价的:

s.age=10
s.set_age(100)

当你调用set_age这个函数时,它的第一个参数self现在就是前面的s,然后进入这个函数内,将self替换为s,你就会发现这两句代码完全一样。

这就是类最简单、最基础的用法了:封装方法函数

而且你应该是能感受得到这种方法的优点的,如果感受不到,你可以试试不用类来实现这个试一试:

比如我需要五个学生变量,使用类的话,我只需要调用Student()五次就行了,不使用类,你就得分别声明五个nameage变量,这还只是两个变量的情况下,如果更多,那就更加复杂了。

三、初始化函数

其实如果你稍微思索一下就会发现,像上面那样写是有问题的,比如如果我不调用set_name这些函数、也不初始化这两个变量,直接调用fmt函数怎么办呢?

s=Student()
print(s.fmt())

这时候肯定就会报错了!因为你还没有为其绑定对应的变量,而fmt函数中却要使用这两个变量,没有,那肯定就报错了!

image-20240309124528124

为了解决这个问题,就出现了初始化函数(也称为构造函数):

    def __init__(self):
        self.name=""
        self.age=0

这个函数名字是固定的,它的第一个参数同样是self,写在对应的类中。

它与普通的成员函数唯一不同的一点是:它会默认调用

也就是说,这个函数不需要你手动调用,只要你实例化了一个这个类的对象,那就会立马自动调用这个函数。

这就完成了初始化的工作!

class Student:
    def __init__(self):
        self.name = ""
        self.age = 0

    def set_name(self, name: str):
        self.name = name

    def set_age(self, age: int):
        self.age = age

    def fmt(self):
        return self.name + " " + str(self.age)


s = Student()
print(s.fmt())

此时就不会出现任何问题了!

但既然能自动初始化,那是不是可以再简化一点呢?比如在实例化一个对象时,就立即初始化好一个我们想要的对象!

这当然是可以的!

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

你只需要像普通函数那样,为其写上参数就好了。

然后在实例化一个对象时,就可以像下面这样做:

s = Student(name='yushi',age=100)
print(s.fmt())

是不是感觉和函数调用特别像!

四、规范说法

前面为了好理解,说的可能比较笼统,所以这里规范一下他们的名称:

  • :封装属性、方法的抽象化类型(可以理解为一个自定义的类型,比如int、str等)
  • 对象:为类的具体表现形式(就和人类与你的区别,一个大而抽象、一个小而具体),拥有具体的内存,一般称为实例化一个对象
  • 属性:也就是绑定到类上面的那些变量,称为属性
  • 方法:也就是绑定到类上面的那些函数

五、类的特性

对于类这个概念来说,一般有三个特性:封装继承多态

其中的封装,前面已经提到了,就是将关联的变量与函数统一到一个类中。

但那会暴露全部方法,并不完全封装。

有些方法可能是我们这个类自己用的,并不想要暴露出去让对象直接调用,所以就需要将其私有化,这在python中非常简单,就是在方法名前添加两个下划线_

    def __test(self):
        self.name ='test'
作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux