Python面向对象
我们前面介绍的数据结构和运算都是面向过程的,而Python本身设计之初就是面向对象的编程语言,本节我们学习Python中的面向对象编程。
面向过程VS面向对象
面向过程是以事件为中心的编程思想,按照完成这件事需要的所有步骤,强调按部就班,一个函数一个函数、一个语句一个语句的顺序执行。
以下五子棋为例,一个下五子棋的面向过程程序大约需要以下步骤:
- 开始游戏;
- 黑子先行,绘制黑子落子后的画面;
- 判断输赢情况,如果黑子赢了就跳到最后一步;
- 白子再行,绘制白子落子后的画面;
- 判断输赢情况,如果白子赢了就跳到最后一步;
- 返回第二步;
- 输出最后结果。
而面向对象的五子棋程序会首先设计三种互相独立的对象:
- 棋手,包括黑子和白子,负责接收用户的输入,并告知棋盘系统棋子布局的变化;
- 棋盘系统,负责接收棋手传递的用户落子情况,然后绘制画面,在屏幕显示;
- 游戏规则,负责根据棋盘情况判定输赢。
看起来没有太大区别,只是对面向过程的内容做了一些封装,然而如果我们要写的代码是运行一个公司呢?每个行为方的行为规则和和属性可能都复杂到面向过程难以描述,描述了别人也难以快速看懂、快速修改和扩展。
面向对象的编程极大提高了代码的可扩展性和复用性,降低了维护难度,在写一些大型的工程时具有很明显的优势,二者对比如下表。
面向过程 | 面向对象 |
---|---|
高耦合 | 低耦合 |
高效率,低开销,低可读性 | 低效率,高开销,高可读性 |
难维护,难扩展 | 易维护,易扩展 |
面向对象简介
面向对象技术中主要有以下几个概念:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法;
- 对象(Object):是类的实例;
- 方法:类中定义的函数;
- 实例变量:实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问;
- 类变量:类变量指的是在类中,但在各个类方法外定义的变量,所有类的实例化对象都同时共享类变量,可以通过类名或者对象名进行访问;
- 保护:包括保护变量和保护方法。以单下划线开头,表示被保护的变量和方法只允许类及其子类使用,在使用中与普通变量没什么区别;
- 私有:包括私有变量和私有方法。私有变量和私有方法只有类中定义的方法可以访问,子类不可以访问;
- 实例化:创建一个类的实例,类的具体对象;
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。
我们来定义一个简单的类:
class People:
# id 是一个普通的类变量
id = 1
# 单下划线开头的是保护变量,_protected1是受保护的类变量
# 保护变量表示只允许类及其子类使用,然而在使用中与普通变量没什么区别
_protected1 = 2
# 双下划线开头的是私有变量,__private1是私有的类变量
# 私有变量只有类中定义的方法可以访问,子类不可以访问
__private1 = 3
# __init__()是类的构造函数,实在一个类进行实例化的时候自动调用的函数,可以传入参数
# 以双下划线开头且以双下划线结尾的函数在Python中表示特殊功能的函数,我们在写代码时尽量不要写这种形式的函数名
def __init__(self, para1, para2):
# name gender 是实例变量
self.name = para1
self.gender = para2
# _protected2是受保护的实例变量
self._protected2 = True
# __private2是私有的类变量
self.__private2 = True
# 类中的一个方法,类中定义的方法的参数都必须包含self且以self开头
# self表示的是实例化的对象
def prtname(self):
print(self.name)
# 类中的一个方法
def prtgender(self):
print(self.gender)
# 私有变量只有类中定义的方法可以访问1
def prtprivate(self):
print(self.__private1)
print(self.__private2)
def _prtgender(self):
print(self.gender)
def __privatefunc(self):
print(self.__private1)
print(self.__private2)
# 实例化People类,得到一个对象x
x = People("gs", "1")
# 调用类变量可以通过对象和类
print(People.id)
print(People._protected1)
# 下面注释掉的几句是错误的
# print(People.__private1)
# print(People.name)
# print(People.gender)
# print(People._protected2)
# print(People.__private2)
print(x.id)
print(x._protected1)
print(x.name)
print(x.gender)
print(x._protected2)
# 下面注释掉的是错误的
# print(x.__private1)
# print(x.__private2)
# Python并没有从语法上严格保证私有属性或方法的私密性
# Python还提供了一种作弊的方法用于访问私有变量,Python寄希望于使用者自觉遵守规则并为自己的行为负责。
#“We are all consenting adults here”
print(x._People__private1)
print(x._People__private2)
# 调用类中的方法
x.prtname()
x.prtgender()
# private变量只有类中定义的方法可以调用
x.prtprivate()
这段代码的注释中基本写明了各种变量和函数的用法。下面我们学习继承的概念,帮助我们更好地理解类的用法。
类的继承
一个类可以继承另一个类,如类Student可以继承类People,此时Student称为子类,People称为父类。子类可以继承父类的普通变量与方法,以及保护变量与方法。
现在我们基于上一小节的代码继续往下写一个People类的子类Student,看看下面这段代码中,为什么有的调用正确,有的调用错误。
class Student(People):
def __init__(self, para1, para2, para3):
People.__init__(self, para1, para2)
self.school = para3
s = Student("gs", "1", 3)
print(s.school)
print(s.id)
print(s._protected1)
print(s.name)
print(s.gender)
print(s._protected2)
s.prtname
s._prtgender()
s.prtprivate()
# 下面注释掉的几句是错误的
# print(s.__private1)
# print(s.__private2)
# s.__privatefunc()
当然,一个类可以有很多个子类,子类还可以有子类,可以无限继承下去。
一个子类也可以有多个父类,在初始化时需要对各个父类都做初始化。
另外如果子类中定义了与父类相同名称的函数,在子类的一个实例对象调用该函数时,就会优先调用子类中的该函数,这就是“多态”。即,对于父类的方法,一个子类可以做无限修改,而不影响其他子类,也不影响依赖于该父类和该方法的其他函数。
以一张图结束本节课程吧,不知道小仙女有没有懂那么一丢丢呀😄😄😄