四.面向对象编程
在学习python这门编程语言之初,我们所编写的代码大都是使用的指令式编程,指令式编程是面向过程(函数)编程,当编写的程序比较简单时是没有任何毛病的,反之可能就没那么好用了,此时就该讨论另外一种编程范式了——面向对象编程
面向对象的相关概念
类 : 将一大类具有共同特征(静态/动态)的对象的共同特征抽取出来后得到的一个抽象概念。简单说,类是对象的蓝图(模板),有了类才能创建出这种类型的对象。类定义了具有共同特征的对象所共有的属性和方法
对象 : 对象是类的具体实例,是可以接收消息的实体。面向对象编程就是通过给对象发消息达到解决问题的目标
对象=数据+函数(方法)---->对象将数据和操作数据的函数从逻辑上变成了一个整体
简单学习面向对象的编程,主要有三个步骤:定义类,造对象,发消息 ;这三个步骤可以说泛化的说明了面向对像编程从编写创建到使用的过程
第一步:类的定义
定义类时使用关键字class,其后跟类名ClassName和冒号,类名定采用驼峰式命名法:
定义类时主要有两个步骤要点:
数据抽象:找到和对象相关的静态特征(即属性)
行为抽象:找到和对象相关的动态特征(即方法)
具体代码示例:
class Student:
#数据抽象
def __init__(self,name,age):
self.name=name #绑定属性
self.age=age
# 行为抽象(行为方法)
def eat(self): #接收消息的学生对象
print(f'{self.name}正在吃饭')
def study(self,course_name):
print(f'{self.name}正在学习{course_name}.')
def play(self,game_name):
print(f'{self.name}正在玩{game_name}. ')
def watch_av(self):
if self.age<18:
print(f'{self.name}未满18岁,只能看熊出没')
else:
print(f'{self.name}正在观看岛国')
如上,在数抽象的时候会用到一个名为 init() 的特殊方法(构造方法),该方法在类实例化的时候会自动调用;在类的内部,我们使用def关键字来定义类的方法,与一般函数定义不同的是,类方法中必须包含参数self,且为第一个参数,self代表的是类的一个具体实例
第二,三步:创建对象,给对象发消息
#第二步:创建对象-->构造器语法--->类名(...,...)
stu1=Student('王大锤',15)
stu2=Student('小明',23)
#第三步:给对象发消息(调用对象)
stu1.study('python')
Student.study(stu1,'python')
stu1.eat()
stu2.play('斗地主')
stu1.name='王锤'
stu1.eat()
"""运行结果"""
# 王大锤正在学习python.
# 王大锤正在学习python.
# 王大锤正在吃饭
# 小明正在玩斗地主.
# 王锤正在吃饭
在类里面写的函数,我们通常称之为方法,他们基本上都是发给对象消息,但有时候,我们并不想把消息发给对象,而是希望发给这个类(注意:类本身也是一个对象),此时我们会用到静态方法或者是类方法。静态方法和类方法的作用是一样的效果,不同的是他们的装饰器
静态方法------发送给类消息------>@staticmethod
类方法--------->发送给类消息------>@classmethod
代码示例如下:
给定构造三角形的三条边,判断若能构成三角形,则求该三角形的周长和面积
class Triangle:
def __init__(self,a,b,c):
if not Triangle.is_valid(a,b,c):
raise ValueError('无效边长')
self.a=a
self.b=b
self.c=c
'''@classmethod
def is_valid(cls,a,b,c): 此方法与下作用相同,不同的是定义的时候需要加上cls
return a+b>c and a+c>b and b+c>a
'''
@staticmethod
def is_valid(a,b,c):
"""判断三条边是否有效"""
return a+b>c and a+c>b and b+c>a #返回True
def perimeter(self):
"""求三角形周长"""
return self.a+self.b+self.c
def area(self): #海伦公式
"""求三角形面积"""
half = self.perimeter() / 2
return half * (half-self.a)*(half-self.b)*(half-self.c)
if __name__ == '__main__':
try:
t=Triangle(4,3,4)
print(t.perimeter())
print(t.area())
except ValueError as err:
print(err)
大概了解了面向对象编程从创建到使用的简单步骤之后,下面我们需要对面向对象的一些知识进行扩充
面向对象有四大特性:抽象,封装,继承,多态
1.抽象 : 在定义类的时候体现(数据抽象,行为抽象),忽略一个事物中与当前目标无关的方面,只注意与当前目标有关的方面
2.封装 :把内部细节性的东西隐藏起来,只暴露与外界调用的简单接口
3.继承 :从已经存在的类中继承信息创建新类的过程
4.多态 :相同或不同子类型的对象对同一消息作出不同响应
面向对象编程重要的方法——继承,先看代码示例:
class Person:
def __init__(self, name, age):
self.name = name # 绑定属性
self.age = age
# 行为抽象(行为方法)
def eat(self): # 接收消息的学生对象
print(f'{self.name}正在吃饭')
def play(self,game_name):
print(f'{self.name}正在玩{game_name}. ')
class Teacher(Person): #继承
def __init__(self, name, sex, teach_name, age):
super().__init__(name, age)
self.teach_name = teach_name
def teach(self):
print(f'{self.name}正在教授{self.teach_name}课')
teacher = Teacher('王', '女', '语文','23')
teacher.eat()
teacher.play('斗地主')
teacher.teach()
'''结果输出'''
# 王正在吃饭
# 王正在玩斗地主.
# 王正在教授语文课
从上面的代码中,我们首先创建了一个Person的类,然后再创建了一个Teacher的类,与之前我们定义类不同的是类名后多了一个括号及里面指定了一个已经创建的类的类名,这样定义类的方式,我们称之继承,括号内是该类继承的类,称之为父类(超类,基类),继承者被称为子类。在继承父类所有的属性和方法时,我们不需要在子类重写父类所拥有的方法,还可以在子类中添加父类中没有的属性和方法。
我们再接着来看一个例子:
编写一个工资结算系统:
-
要求:输入员工信息,自动结算员工工资,其中员工有以下三类
- 部门经理:固定月薪,15000元
- 程序员:计时结算月薪,每小时200元
- 销售员:底薪+提成,底薪1800元,销售额5%提成
class Employee:
def __init__(self, no, name):
self.no = no
self.name = name
def get_salary(self):
pass
class Manager(Employee):
def get_salary(self):
return 15000
class Programmer(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.working_hour = 0
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.sales = 0
def get_salary(self):
return 1800 + 0.05 * self.sales
def main():
emps = [
Manager(1122, '刘备'), Programmer(2233, '诸葛亮'),
Salesman(3344, '关羽')
]
for emp in emps:
if type(emp) == Programmer:
emp.working_hour = int(input(f'请输入{emp.name}本月工作时长: '))
elif type(emp) == Salesman:
emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
print(f'{emp.name}本月工资: {emp.get_salary()}元')
if __name__ == '__main__':
main()
'''输出结果'''
# 刘备本月工资: 15000元
# # 请输入诸葛亮本月工作时长: 23
# # 诸葛亮本月工资: 4600元
# # 请输入关羽本月销售额: 45
# # 关羽本月工资: 1802.25元
从以上的代码实例中,我们可以看到三种员工类都继承了Employee这一个父类,父类有一个带重写方法get_salary,这个方法在每个子类中都进行了重写,使用了相同的方法名,但实现的功能不一样,这种就是面向对象四大特征中的多态。
补充:
两个类之间的可能关系
-
is-a关系:继承——譬如子类和父类的关系,子类继承父类的属性方法
-
has-a关系:关联——把一个类的对象作为另一个类的对象的属性。大致有以下两种关联:
普通关联
整体和部分的关联——强关联关系(聚合和合成)
-
use-a关系:依赖——一个类的对象作为另一个类的方法和返回值