一、前言
类同样是编程语言中非常常见的一个概念,如果你想要对类有更加宏观的认识,可以参考本站的另一篇文章:结构体、类、接口。
为了照顾新手,我们还是来简单说一下。
从前面的代码中我们可以发现,写代码无非就是写两个内容:变量、函数。
- 变量:用来存放数据
- 函数:一系列代码语句的集合,一般用来封装一系列常用的代码语句。
显然,这两部分是分开的,很多时候这种分离是合理的,比如前面的提到的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()
就能返回一个这个类型的变量。
由于这是一个类,它内部是绑定了函数的,所以你就可以直接用点的方式使用这些函数:
这个时候,你就可以看到IDE的提示信息了,蓝色图标代指这是一个变量,紫色图标代指这是一个函数。
其中还有大量的
__*__
的变量、函数,这是python为每个类自动生成的东西,也可以调用(具体来说涉及到了类的继承,所有类都默认继承自Object
,这些多出来的函数、变量也是这个类身上的)。
所以下面两句代码其实是等价的:
s.age=10
s.set_age(100)
当你调用set_age
这个函数时,它的第一个参数self
现在就是前面的s
,然后进入这个函数内,将self
替换为s
,你就会发现这两句代码完全一样。
这就是类最简单、最基础的用法了:封装方法、函数
而且你应该是能感受得到这种方法的优点的,如果感受不到,你可以试试不用类来实现这个试一试:
比如我需要五个学生变量,使用类的话,我只需要调用
Student()
五次就行了,不使用类,你就得分别声明五个name
、age
变量,这还只是两个变量的情况下,如果更多,那就更加复杂了。
三、初始化函数
其实如果你稍微思索一下就会发现,像上面那样写是有问题的,比如如果我不调用set_name
这些函数、也不初始化这两个变量,直接调用fmt
函数怎么办呢?
s=Student()
print(s.fmt())
这时候肯定就会报错了!因为你还没有为其绑定对应的变量,而fmt
函数中却要使用这两个变量,没有,那肯定就报错了!
为了解决这个问题,就出现了初始化函数(也称为构造函数):
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'