面向对象技术简介

类(class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

实例化:创建一个类的实例,类的具体对象。

类方法:类中定义的函数。

类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

创建类

在Python中,定义类是通过​​class​​关键字:

class Student([extendClass]):
pass


​class​​后面紧接着是类名,类名通常时大写字母开头,如果该类是从某个其他类继承下来的,类名后面需要跟上继承自哪个类。

创建一个实例

根据​​Student​​类,创建一个​​Student​​的实例:

stu1 = Student()


可以看出,​​stu1​​就是​​Student​​类的一个实例化对象:

Python面向对象-类和实例_类

可以自由地给一个实例变量绑定属性,比如,给实例​​stu1​​绑定一个​​sname​​属性:

Python面向对象-类和实例_Python_02

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的​​__init__​​方法,在创建实例的时候,就把​​sname​​,​​score​​等属性绑上去:

class Student:
def __init__(self, sname, sorce):
self.sname = sname
self.sorce = sorce


????注意:特殊方法​​__init__​​前后分别有两个下划线。

​__init__​​方法的第一个参数永远是​​self​​ ,表示创建的实例本身。因此,在方法内部就可以把各种属性绑定到​​self​​,因为​​self​​就指向创建的实例本身。

有了​​__init​​方法,在创建实例的时候就需要传入和​​__init__​​方法相匹配的参数。其中​​self​​参数不需要传递,Python会自己把实例的对象传给​​self​​。

Python面向对象-类和实例_数据_03

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量​​self​​,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

数据封装

面向对象编程的一个重要特点就是数据封装。在上面的​​Student​​类中,每个实例就拥有各自的​​name​​和​​score​​这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩。

在类的内部定义一个访问数据的函数,这些函数称为:类的方法。

class Student:
def __init__(self, sname, score):
self.sname = sname
self.score = score

def print_score(self):
print(f"{self.sname}的成绩为:{self.score}")


要定义一个类的方法,除了第一个参数是​​self​​外,其他和普通函数一样。

那么如何调用一个类的方法呢?只需要在实例变量上直接调用,传入除​​self​​ 之外的其他参数:

stu2 = Student("李四", 99)
stu2.print_score()


Python面向对象-类和实例_面向对象_04

这样一来,我们从外部看​​Student​​类,就只需要知道,创建实例需要给出​​name​​和​​score​​,而如何打印,都是在​​Student​​类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

封装的另一个好处是可以给​​Student​​类增加新的方法,比如​​get_grade​​:

class Student(object):
...

def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'


小结

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

访问限制

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的​​name​​、​​score​​属性:

stu1 = Student("张三", 80)
stu1.print_score()
stu1.score = 90
stu1.print_score()


Python面向对象-类和实例_实例_05

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线​​__​​,在Python中,实例的变量名如果以​​__​​开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:

class Student:
def __init__(self, sname, score):
self.__sname = sname
self.__score = score

def print_score(self):
print(f"{self.__sname}的成绩为:{self.__score}")


改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问​​实例变量.__name​​和​​实例变量.__score​​了:

Python面向对象-类和实例_实例_06

这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。

但是如果外部代码要获取name和score怎么办?可以给Student类增加​​get_name​​和​​get_score​​这样的方法:

class Student(object):
...

def get_name(self):
return self.__name

def get_score(self):
return self.__score


Python面向对象-类和实例_面向对象_07

如果又要允许外部代码修改score怎么办?可以再给Student类增加​​set_score​​方法:

class Student(object):
...

def set_score(self, score):
self.__score = score


Python面向对象-类和实例_数据_08

这样做还有一个好处,就是可以对参数进行检查,避免传入无效的参数:

    def set_score(self, score):
if score < 0 or score > 100:
print("参数无效")
else:
self.__score = score


Python面向对象-类和实例_Python_09

注意下面这种错误写法:

Python面向对象-类和实例_面向对象_10

表面上看来,“成功”地设置了​​__sname​​变量,但实际上这个​​__sname​​和class内部的​​__sname​​并不是同一个变量,上面的代码只是给实例​​stu1​​新增了一个属性,命名为​​__sname​​,而内部的私有变量​​__sname​​已经被Python解释器自动更改成了其他的名字。