面向对象
浏览 2158
课文
面向对象编程
面向对象编程这个概念,刚接触过编程的同学可能都会听说过。而 python 从设计之初就已经是一门面向对象的语言,也正因为如此我们可以很方便地在 python 中使用面向对象编程的思想。
与面向对象相对的是面向过程编程,传统的 C 语言就是面向过程为主。主要是按照业务流程把梳理代码顺序执行。当业务逻辑清晰,业务规模不大的情况下当然没问题。一旦业务规模庞大,逻辑不清楚,增加需求的时候,开发难度就会曾几何数递增。
面向对象开发中会把对应的事物都抽象成对象的概念,这样子我们在增加对应业务时只需要增加相应的对象类,而无需改变其他类就能达到我们的目的。
面向对象的三大基本特征是:封装
、继承
、多态
。随着这篇文章的深入我们会一一深入学习这三个基本特征。
类与对象
在面向过程的编程方式中,如果我们要定义一些人名与欢迎语,并用方法执行,我们通常会写出以下代码。
xiaoming = {'name': '小明', 'hello': '你好'}
kelly = {'name': 'Kelly', 'hello': 'hello'}
def say_hello(human):
print('{}:{}'.format(human['name'], human['hello']))
say_hello(xiaoming)
say_hello(kelly)
小明:你好呀
Kelly:hello
在人物数量不多时,代码结构还是很清晰的,可是一旦人物数量变多了,就是一个灾难。
xiaoming = {'name': '小明', 'hello': '你好'}
kelly = {'name': 'Kelly', 'hello': 'hello'}
xiaoli = {'name': '小李', 'hello': '雷猴啊'}
lotus = {'name': 'Lotus', 'helo': 'bonjour'}
def say_hello(human):
print('{}:{}'.format(human['name'], human['hello']))
say_hello(xiaoming)
say_hello(kelly)
say_hello(xiaoli)
say_hello(lotus)
小明:你好
Kelly:hello
小李:雷猴啊
Traceback (most recent call last):
File ".\first.py", line 14, in <module>
say_hello(lotus)
File ".\first.py", line 8, in say_hello
print('{}:{}'.format(human['name'], human['hello']))
KeyError: 'hello'
大量的重复代码已经是个灾难,一不小心写错一个字母还引起了程序的崩溃。此时如果再加上 say_bye
等几个方法,代码结构将会更加混乱不堪。
接着我们用面向对象的思维来改造我们的代码,类的定义一定程度上就对应着基本特征封装
。
class Human:
# 定义了一个类
def __init__(self, name, hello):
# 构造函数
self.name = name
self.hello = hello
def say_hello(self):
# 类的方法
print('{}:{}'.format(self.name, self.hello))
# 实例化我们的对象
xiaoming = Human('小明', '你好呀')
kelly = Human('Kelly', 'hello')
xiaoli = Human('小李', '雷猴啊')
lotus = Human('Lotus', 'bonjour')
xiaoming.say_hello()
kelly.say_hello()
xiaoli.say_hello()
lotus.say_hello()
小明:你好呀
Kelly:hello
小李:雷猴啊
Lotus:bonjour
类
(Class)是用来描述具有相同属性
(Attribute)和方法
(Method)对象的集合。对象
(Object)是类
(Class)的具体实例。
此时的 Human
就是我们定义的类,从面向对象编程来说就相当于封装了一个类。然后通过Human
这个类创造了两个对象 xiaoming
与 kelly
。
在 python
中我们通过 class
来定义一个类,指定了类应当有的属性与方法。将相关的属性
(Attribute)与方法
(Method)封装到对象中,是来源于现实世界的一个设计思想。因为现实世界中的任何物体都有其对应的属性
(组成部分)与方法
(功能作用)。
比如一个手机,它有屏幕、处理器、摄像头等等属性,有打电话、玩游戏、看电影等等方法。一个人,有姓名、身高、学历等等属性,有交流、工作、学习等等方法。
回到我们的代码,__init__
是一个构造函数,它要求在实例化的时候传入 name
与 hello
两个参数,并将其保存为自身的属性。这里的 self
代表着一个自身的实例对象。
xiaoming = Human('小明', '你好呀')
意味着实例化了一个 Human
对象。接着我们调用 xiaoming.say_hello()
便完成了对其对象方法的调用。
私有属性与私有方法 Private attribute and private method
一个生活中的人,你自然不会在脑门上看到他的名字,与说你好的方式。这些信息只会在你们初次交流时才可以得到。那我们就可以将 name
、hello
包括 say_hello
设置为私有属性与私有方法。而只对外面暴露一个 contact
方法。
class Human:
# 定义了一个类
def __init__(self, name, hello):
# 构造函数
self.__name = name
self.__hello = hello
def __say_hello(self):
# 类的方法
print('{}:{}'.format(self.__name, self.__hello))
# 实例化我们的对象
xiaoming = Human('小明', '你好呀')
xiaoming.__say_hello() # 这行代码会报错
Traceback (most recent call last):
File ".\first.py", line 16, in <module>
xiaoming.__say_hello() # 这行代码会报错
AttributeError: 'Human' object has no attribute '__say_hello'
python
中通过在属性名与方法名前加上 __
双下划线来定义私有成员,此时的私有成员便不再能从类外访问。我们可以再对其增加一个 contact
方法供外部调用。
class Human:
# 定义了一个类
def __init__(self, name, hello):
# 构造函数
self.__name = name
self.__hello = hello
def __say_hello(self):
# 类的方法
print('{}:{}'.format(self.__name, self.__hello))
def contact(self):
self.__say_hello()
# 实例化我们的对象
xiaoming = Human('小明', '你好呀')
xiaoming.contact()
小明:你好呀
以上体现了封装的理念,只对外暴露需要的接口,而对不必要的信息进行隐藏。
类的继承
通过继承,我们可以重用父类的属性与方法。
class Human:
def __init__(self, name, hello):
self.__name = name
self.__hello = hello
def __say_hello(self):
print('{}:{}'.format(self.__name, self.__hello))
def contact(self):
self.__say_hello()
class Programmer(Human):
def code(self):
print('我正在快乐地编程')
class Fireman(Human):
def outfire(self):
print('我正在跟火灾作斗争')
xiaoming = Programmer('小明', '你好呀')
xiaoli = Fireman('小李', '雷猴啊')
xiaoming.contact()
xiaoming.code()
xiaoli.contact()
xiaoli.outfire()
小明:你好呀
我正在快乐地编程
小李:雷猴啊
我正在跟火灾作斗争
此时我们便已经定义了两个子类 Programmer
与 Fireman
都继承自 Human
类,既重用了父辈的方法与属性,又各自新增了自己独有的方法。
多态
通过多态,我们可以在不同的子类对同一个方法实现不同功能。
class Human:
# 定义了一个类
def __init__(self, name, hello):
# 构造函数
self.__name = name
self.__hello = hello
def __say_hello(self):
# 类的方法
print('{}:{}'.format(self.__name, self.__hello))
def contact(self):
self.__say_hello()
def work():
raise NotImplementedError()
class Programmer(Human):
def contact(self):
super().contact() # 调用父类的方法
print('我是一个快乐的程序员')
def work(self):
print('我正在快乐地编程')
class Fireman(Human):
def contact(self):
super().contact() # 调用父类的方法
print('我是一个消防员,遇火灾联系我!')
def work(self):
print('我正在跟火灾作斗争')
xiaoming = Programmer('小明', '你好呀')
xiaoli = Fireman('小李', '雷猴啊')
xiaoming.contact()
xiaoming.work()
xiaoli.contact()
xiaoli.work()
小明:你好呀
我是一个快乐的程序员
我正在快乐地编程
小李:雷猴啊
我是一个消防员,遇火灾联系我!
我正在跟火灾作斗争
我们重写了其中的 contact
方法。通过 super()
获取到了父类并调用了其 contact
方法。
我们同样在父类中定义了 work
方法,raise NotImplementedError()
的意思是在调用 work
方法前必须对其进行重写,不然会报错。 于是我们在子类中根据 Programmer
类与 Fireman
类的特性,分别对 work
方法进行了重写。
这时多态
的效果便已达到,针对同样继承的父类,不同的子类在同一方法
上进行了不同的实现
。
带来的好处是显而易见的,针对所有继承自 Human
类的子类,我们都可以直接调用他的 work
方法让他去工作,而无需关心内部的实现。
实战
1.定义一个女(男)朋友类,类至少包含姓名
、国籍
两个属性, 问好
、约会
两个方法。并将其实例化。
开放式问题,答案不唯一。
解析
- 定义一个女(男)朋友类,类至少包含
姓名
、国籍
两个属性,问好
、约会
两个方法。并将其实例化。
开放式问题,答案不唯一。
class Friend:
def __init__(self, name, country):
self.name = name
self.country = country
def say_hello(self):
print('%s向你问好' % self.name)
def date(self):
print('约会吗?我在%s等你' % self.country)
xiaofang = Friend(name='小芳', country='日本')
xiaoming = Friend(name='小明', country='美国')
xiaofang.say_hello()
xiaofang.date()
xiaoming.say_hello()
xiaoming.date()
小芳向你问好
约会吗?我在日本等你
小明向你问好
约会吗?我在美国等你
小和
2022-09-04