Python基础day10面向对象、模块、异常(上)

120 阅读15分钟
练习:设计类


1. 设计一个卖车的4S店,该怎样做呢?

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# 定义车类class Car(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个销售车的店类class CarStore(object):
def order(self):
car = Car() #找一辆车
return car
# 1. 先的有个销售汽车的店铺
car_store = CarStore()# 2. 通过这家店铺,订购车
my_car = car_store.order()# 3. 开车爽。。。
my_car.move()
my_car.stop()



说明

上面的
4s店,只能销售一种类型的车

如果这个是个销售北京现代品牌的车,比如伊兰特、索纳塔等,该怎样做呢?


2. 设计一个卖北京现代车的4S店


[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 定义伊兰特车类class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个销售北京现代车的店类class CarStore(object):
def order(self, typeName):
#根据客户的不同要求,生成不同的类型的车
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
xiandai_store = CarStore()
my_car = xiandai_store.order("索纳塔")
my_car.move()
my_car.stop()

这样做,不太好,因为当北京现代又生产一种新类型的车时,那么又得在
CarStore类中修改,有没有好的解决办法呢?
[size=21.3333px]

工厂模式


1. 简单工厂模式

在上一节中,最后留下的个问题,该怎样解决呢?



1.1.使用函数实现

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 定义伊兰特车类class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个函数,来模拟一个汽车厂,目的是创建出具体的汽车对象def createCar(typeName):
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
# 定义一个销售北京现代车的店类class CarStore(object):
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
car = createCar(typeName)
return car
xiandai_store = CarStore()
my_car = xiandai_store.order("索纳塔")
my_car.move()
my_car.stop()


1.2.使用类来实现

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 定义伊兰特车类class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义一个生产汽车的工厂,让其根据具体的订单生产车class CarFactory(object):
def createCar(self,typeName):
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
# 定义一个销售北京现代车的店类class CarStore(object):
def __init__(self):
#设置4s店的指定生产汽车的工厂
self.carFactory = CarFactory()
def order(self, typeName):
# 让工厂根据类型,生产一辆汽车
car = self.carFactory.createCar(typeName)
return car
xiandai_store = CarStore()
my_car = xiandai_store.order("索纳塔")
my_car.move()
my_car.stop()



咋一看来,好像只是把生产环节重新创建了一个类,这确实比较像是一种编程习惯,此种解决方式被称作简单工厂模式

工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,
4s店只负责销售,汽车厂只负责制造


2. 工厂方法模式



多种品牌的汽车
4S店


当买车时,有很多种品牌可以选择,比如北京现代、别克、凯迪拉克、特斯拉等,那么此时程序又该怎样进行设计呢?


[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 定义一个基本的4S店类class CarStore(object):
#仅仅是定义了这个方法,并没有实现具体功能,这个需要在子类中实现
def createCar(self, typeName):
pass
def xiChe(self):
pass
# 省略洗车的过程....>> self.car.x()
def shangBaoXian(self):
pass
# 省略上保险的过程....>> self.car.xx()
def shangChePai(self):
pass
# 省略上车牌的过程....>> self.car.xxx()
def order(self, typeName):
# 1. 让工厂根据类型,生产一辆汽车
self.car = self.createCar(typeName)
# 2. 洗车
self.xiChe()
# 3. 上保险
self.shangBaoXian()
# 4. 上车牌
self.shangChePai()
# 5. 将车返回给用户
return self.car
# 定义一个北京现代4S店类class XiandaiCarStore(CarStore):
def __init__(self):
#设置4s店的指定生产汽车的工厂
self.carFactory = XianDaiFactory()
# 重写createCar方法,从而完成现代车具体的生成过程,
# 而其他的像洗车、上牌的功能,都是直接继承自父类,从而让程序更简单了
def createCar(self, typeName):
return self.carFactory.createCar(typeName)
# 定义一个生产汽车的工厂,让其根据具体得订单生产车class XianDaiFactory(object):
def createCar(self, typeName):
if typeName == "伊兰特":
car = YilanteCar()
elif typeName == "索纳塔":
car = SuonataCar()
return car
# 定义伊兰特车类class YilanteCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
# 定义索纳塔车类class SuonataCar(object):
# 定义车的方法
def move(self):
print("---车在移动---")
def stop(self):
print("---停车---")
suonata = XiandaiCarStore()
my_car = suonata.order("索纳塔")
my_car.move()
my_car.stop()


最后来看看
工厂方法模式
的定义


定义了一个创建对象的
接口
(可以理解为函数),但由子类决定要实例化的类是哪一个,工厂方法模式让类的实例化推迟到子类,抽象的CarStore提供了一个创建对象的方法createCar,也叫作
工厂方法

子类真正实现这个
createCar方法创建出具体产品。 创建者类不需要直到实际创建的产品是哪一个,选择了使用了哪个子类,自然也就决定了实际创建的产品是什么。
[size=21.3333px]

__new__
方法


__new__和__init__
的作用

[Python]
纯文本查看
复制代码
1
2
3
4
5
6
7
8
9
class A(object):
def __init__(self):
print("这是 init 方法")
def __new__(cls):
print("这是 new 方法")
return object.__new__(cls)
A()


总结
[size=10.0000pt]·
__new__
至少要有一个参数
cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供

__new__
必须要有返回值,返回实例化出来的实例,这点在自己实现
__new__
时要特别注意,可以
return父类
__new__
出来的实例,或者直接是
object的
__new__
出来的实例

__init__
有一个参数
self,就是这个
__new__
返回的实例,
__init__
__new__
的基础上可以完成一些其它初始化的动作,
__init__
不需要返回值

我们可以将类比作制造商,
__new__
方法就是前期的原材料购买环节,
__init__
方法就是在有原材料的基础上,加工,初始化商品环节
[size=10.0000pt]


注意点



单例模式


1. 单例是什么

举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。


2. 创建单例-保证只有1个对象

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
# 实例化一个单例class Singleton(object):
__instance = None
def __new__(cls, age, name):
#如果类属性__instance的值为None,
#那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
#能够知道之前已经创建过对象了,这样就保证了只有1个对象
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton(18, "dongGe")
b = Singleton(8, "dongGe")
print(id(a))
print(id(b))
a.age = 19 #给a指向的对象添加一个属性
print(b.age)#获取b指向的对象的age属性

运行结果:

In [
12
]:
class
Singleton
(object)
:

...: __instance =
None

...:

...:
def
__new__
(cls, age, name)
:

...:
if
not
cls.__instance:

...: cls.__instance = object.__new__(cls)

...:
return
cls.__instance

...:

...: a = Singleton(
18
,
"dongGe"
)

...: b = Singleton(
8
,
"dongGe"
)

...:

...: print(id(a))

...: print(id(b))

...:

...: a.age =
19

...: print(b.age)

...:
4391023224439102322419

3. 创建单例时,只执行1次__init__方法

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 实例化一个单例class Singleton(object):
__instance = None
__first_init = False
def __new__(cls, age, name):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, age, name):
if not self.__first_init:
self.age = age
self.name = name
Singleton.__first_init = True
a = Singleton(18, "dongGe")
b = Singleton(8, "dongGe")
print(id(a))
print(id(b))
print(a.age)
print(b.age)
a.age = 19
print(b.age)



运行结果
:

[size=21.3333px]

异常

<1>异常简介


看如下示例
:

[Python]
纯文本查看
复制代码
1
2
3
print '-----test--1---'
open('123.txt','r')
print '-----test--2---'


运行结果
:




说明
:

打开一个不存在的文件
123.txt,当找不到123.txt 文件时,就会抛出给我们一个IOError类型的错误,No such file or directory:123.txt (没有123.txt这样的文件或目录)



异常
:

Python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"

[size=21.3333px]

案例剖析

<1>捕获异常 try...except...


看如下示例
:

[Python]
纯文本查看
复制代码
1
2
3
4
5
try:
print('-----test--1---')
open('123.txt','r')
print('-----test--2---')except IOError:
pass

运行结果
:


说明
:

·
此程序看不到任何错误,因为用
except 捕获到了IOError异常,并添加了处理的方法

·
pass 表示实现了相应的实现,但什么也不做;如果把pass改为print语句,那么就会输出其他信息

小总结
:

·

·
把可能出现问题的代码,放在
try中

·
把处理异常的代码,放在
except中


<2> except捕获多个异常


看如下示例
:


[Python]
纯文本查看
复制代码
1
2
3
try:
print numexcept IOError:
print('产生错误了')

运行结果如下
:


想一想
:

上例程序,已经使用
except来捕获异常了,为什么还会看到错误的信息提示?

:

except捕获的错误类型是IOError,而此时程序产生的异常为 NameError ,所以except没有生效

修改后的代码为
:


[Python]
纯文本查看
复制代码
1
2
3
try:
print numexcept NameError:
print('产生错误了')

运行结果如下
:


实际开发中,捕获多个异常的方式,如下:

[Python]
纯文本查看
复制代码
1
2
3
4
5
6
7
#coding=utf-8try:
print('-----test--1---')
open('123.txt','r') # 如果123.txt文件不存在,那么会产生 IOError 异常
print('-----test--2---')
print(num)# 如果num变量没有定义,那么会产生 NameError 异常
except (IOError,NameError):
#如果想通过一次except捕获到多个异常可以用一个元组的方式

注意:
·
当捕获多个异常时,可以把要捕获的异常的名字,放到
except 后,并使用元组的方式仅进行存储


<3>获取异常的信息描述





<4>捕获所有异常





<5> else
咱们应该对
else
并不陌生,在
if中,它的作用是当条件不满足时执行的实行;同样在try...except...中也是如此,即如果没有捕获到异常,那么就执行else中的事情


[Python]
纯文本查看
复制代码
1
2
3
4
5
try:
num = 100
print numexcept NameError as errorMsg:
print('产生错误了:%s'%errorMsg)else:
print('没有捕获到异常,真高兴')

运行结果如下
:



<6> try...finally...


try...finally...语句用来表达这样的情况:

在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用
finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等

demo:


[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import timetry:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
time.sleep(2)
print(content)
except:
#如果在读取文件的过程中,产生了异常,那么就会捕获到
#比如 按下了 ctrl+c
pass
finally:
f.close()
print('关闭文件')except:
print("没有这个文件")

说明
:

test.txt文件中每一行数据打印,但是我有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些。在程序运行的时候,按Ctrl+c中断(取消)程序。

我们可以观察到
KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。
[size=21.3333px]

异常的传递


1. try嵌套中

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
import timetry:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
time.sleep(2)
print(content)
finally:
f.close()
print('关闭文件')except:
print("没有这个文件")

运行结果
:

In [26]: import time

...: try:

...: f = open('test.txt')

...: try:

...: while True:

...: content = f.readline()

...: if len(content) == 0:

...: break

...: time.sleep(2)

...: print(content)

...: finally:

...: f.close()

...: print('关闭文件')

...: except:

...: print("没有这个文件")

...: finally:

...: print("最后的finally")

...:

xxxxxxx--->这是test.txt文件中读取到信息

^C关闭文件

没有这个文件

最后的
finally

2. 函数嵌套调用中

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def test1():
print("----test1-1----")
print(num)
print("----test1-2----")
def test2():
print("----test2-1----")
test1()
print("----test2-2----")
def test3():
try:
print("----test3-1----")
test1()
print("----test3-2----")
except Exception as result:
print("捕获到了异常,信息是:%s"%result)
print("----test3-2----")
test3()
print("------华丽的分割线-----")
test2()


运行结果
:


总结:

·
如果
try嵌套,那么如果里面的try没有捕获到这个异常,那么外面的try会接收到这个异常,然后进行处理,如果外边的try依然没有捕获到,那么再进行传递。。。

·
如果一个异常是在一个函数中产生的,例如函数
A---->函数B---->函数C,而异常是在函数C中产生的,那么如果函数C中没有对这个异常进行处理,那么这个异常会传递到函数B中,如果函数B有异常处理那么就会按照函数B的处理方式进行执行;如果函数B也没有异常处理,那么这个异常会继续传递,以此类推。。。如果所有的函数都没有处理,那么此时就会进行异常的默认处理,即通常见到的那样

·
注意观察上图中,当调用
test3函数时,在test1函数内部产生了异常,此异常被传递到test3函数中完成了异常处理,而当异常处理完后,并没有返回到函数test1中进行执行,而是在函数test3中继续执行
[size=21.3333px]

抛出自定义的异常


你可以用
raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类

下面是一个引发异常的例子
:


[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
class ShortInputException(Exception):
'''自定义的异常类'''
def __init__(self, length, atleast):
#super().__init__()
self.length = length
self.atleast = atleast
def main():
try:
s = input('请输入 --> ')
if len(s) < 3:
# raise引发一个你定义的异常
raise ShortInputException(len(s), 3)
except ShortInputException as result:#x这个变量被绑定到了错误的实例
print('ShortInputException: 输入的长度是 %d,长度至少应是 %d'% (result.length, result.atleast))
else:
print('没有异常发生.')
main()

运行结果如下
:


注意
·
以上程序中,关于代码
#super().__init__()
的说明

这一行代码,可以调用也可以不调用,建议调用,因为
__init__
方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的
__init__
方法,即意味着父类中的很多初始化工作没有做,这样就不保证程序的稳定了,所以在以后的开发中,如果重写了父类的
__init__
方法,最好是先调用父类的这个方法,然后再添加自己的功能


异常处理中抛出异常

[Python]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test(object):
def __init__(self, switch):
self.switch = switch #开关
def calc(self, a, b):
try:
return a/b
except Exception as result:
if self.switch:
print("捕获开启,已经捕获到了异常,信息如下:")
print(result)
else:
#重新抛出这个异常,此时就不会被这个异常处理给捕获到,从而触发默认的异常处理
raise
a = Test(True)
a.calc(11,0)
print("----------------------华丽的分割线----------------")
a.switch = False
a.calc(11,0)

运行结果
:

更多免费技术资料可关注:annalin1203