人生总是要折腾一下的

简学Python第六章__class面向对象编程与异常处理

 

Python第六章__class面向对象编程与异常处理

欢迎加入Linux_Python学习群

  群号:478616847

 

目录:

  • 面向对象的程序设计

  • 类和对象

  • 封装

  • 继承与派生

  • 多态与多态性

  • 特性property
  • 静态方法与类方法

  • 异常处理

 

一、面向对象的程序设计

在Python中大家一定听过一句话,叫做一切皆对象,字典、序列、数字和字符串都是根据类来创建的,在python中面向对象(object)编程是python的

核心概念,类(class)最终解释了面向对象编程思想(OOP),同样类也是一种对象,它是通过python中的元类(type)创建的,在这个元类中,定义了类

是如何创建的(第七章讲解元类),下面开始学习面向对象的编程。

 

为什么要有面向对象的程序设计?

在学习面向对象之前,我们要确定面向对象的程序设计不是编程,而是设计方法和编程思路,就像我们搭建一个集群,首先要先设计一下,这个服务器

安装什么,那个服务器安装什么,面向对象的编程也是一样的要先设计程序,然后才能进行编程,我们所写的程序都是面向过程的,也就说程序是按照

一定的步骤去解决问题,就好比一条流水线,到了哪个阶段该做什么事,那么这样的优点就是极大地降低了程序的复杂度,缺点就是过程式编程是为了

解决一个问题的,就好比生成自行车的流水线生产不了汽车,面向过程的编程,往往系统会更加稳定,但是面对频繁的需求变更的时候,面向过程就变

得乏力,经常的出现改一个地方,其它地方也需要修改。

 

而面向对象的程序设计核心就是对象,在现实世界中人们的主观上,将实物分成不同的类型,比如动物中有猫类,狗类等等,并且狗也有不同的类型

(比如哈士奇,藏獒),每个个体也有独特的属性(比如体重,毛发的颜色),所以中把这种形式引进到编程,面向对象的编程首先要把自己当成上帝,

当成创造者,我们创建一个类之前需要考虑这个类有什么共同性,有什么差异性,我们可以把这些共同性变成一个类,然后通过这个共同性的类,创

造出差异性的个体。

面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性,并且面向对象编程的可控性差,经常可以看到

游戏中的BUG,比如一刀几千血啊之类的。

1.性能(Performance)是指系统的响应能力,即要经过多长时间才能对某个事件作出响应,或者在某段时间内系统所能处理的事件个数;

2.可用性(Availability)是指系统能够正常运行的时间比例;

3.可靠性(Reliability)是指系统在应用或者错误面前,在意外或者错误使用的情况下维持软件系统功能特性的能力;

4.健壮性(Robustness)是指在处理或者环境中系统能够承受的压力或者变更能力;

5.安全性(Security)是指系统向合法用户提供服务的同事能够阻止非授权用户使用的企图或者拒绝服务的能力;

6.可修改性(Modification)是指能够快速地以较高的性能价格比对系统进行变更的能力;

7.可变性(Changeability)是指体系结构扩充或者变更成为新体系结构的能力;

8.易用性(Usability)是衡量用户使用软件产品完成指定任务的难易程度;

9.可测试性(Testability)是指软件发现故障并隔离定位其故障的能力特性,以及在一定的时间或者成本前提下进行测试设计、测试执行能力;

10.功能性(Function ability)是指系统所能完成所期望工作的能力;

11.互操作性(Inter-Operation)是指系统与外界或系统与系统之间的相互作用能力。

软件的质量属性
软件质量属性

 

二、类和对象 

在python中声明函数与声明类很相似,下面开始来创建第一个类

语法:class  类名:

     类的注释

     类体

1 class Role:
2     pass
创建类

嗯是的只需要这样就创建完成了一个类,当然这样创建时毫无意义的,我是用这个引出别的知识点

首先:规范当中规定了类名首字母必须大写

其次:在python2当中类分为新式类和经典类,python3中都是新市类,python2中新式类需要让类继承object这个父类,并且大家在使用类中

尽量都用新式类,因为新式类中,新增加了一些属性,dir()也变得更健壮。

1 #python2中的新式类
2 class Role(object):
3     pass
4 #python2中的经典类
5 class Role():
6     pass
python2中的类

 

属性与实例化

类的作用有两种,分别是属性的引用和实例化

类属性的操作用 (类名.变量名),类的属性分别数据属性和函数属性(也叫方法属性)

 1 class Role:
 2     region = "China"
 3     def output_region(self):
 4         print("My region is China")
 5         
 6 #引用类的数据属性,该属性是被所有对象和实例共享的 
 7 print(Role.region)
 8 #通过类引用类的函数属性
 9 Role().output_region()
10 #增加类的数据属性
11 Role.name = "Hello"
12 #引用新增加的数据属性
13 print(Role.name)
14 #删除类中的数据属性,注意删除后所有对象和实例都无法调用
15 del Role.name
属性操作

 

实例化

我们说类最终要生成一个对象的,那么类生成对象的过程就是实例化,实例化后的对象,可以操作类中的方法,也可以传入自己独有的属性,这样生成的

对象每个都是个体每个都是独立的,以英雄联盟这个游戏为例,我们创建一个英雄的类,每个玩家操作的英雄都有自己的独有属性例如名字,攻击力和生

命值,并且每个英雄都会普通攻击,而且英雄都有阵营,比如德玛西亚,诺克萨斯,这些阵营是不变的而且每个英雄都有,下面模拟实现上述的需求。

 1 class Galen:
 2     camp = "Demacia"
 3     def __init__(self,name,attack,life):
 4         self.name = name
 5         self.attack = attack
 6         self.life = life
 7     def slogan(self):
 8         print("%s喊了口号:德玛西亚!"%self.name)
 9 
10 class Annie:
11     camp = "Noxus"
12     def __init__(self,name,attack,life):
13         self.name = name
14         self.attack = attack
15         self.life = life
16     def slogan(self):
17         print("%s喊了口号:你是我的小熊么?"%self.name)
18 
19 #实例化一个盖伦
20 G1 = Galen("盖伦",20,100)
21 #实例化一个安妮
22 A1 = Annie("安妮",17,100)
23 
24 G1.slogan()
25 A1.slogan()
样例

首先创建了两个角色,一个是盖伦,一个是安妮,其次它们都有阵营,这个阵营不变的,所以用类的数据属性来定义阵营,然后每个角色都有自己的属性

所以用 __init__ 来进行封装,最后给这两个英雄都有一个功能,就是喊口号,并且实例化了两个角色,而且调用了角色中的喊口号的功能。

 

首先看__init__(构造函数)

好的上面的代码中提到了实例化,那么实例化就是  对象名 = 类名(参数),参数可有可无,但是我们发现 class Galen:并没有要传入参数啊

反而__init__中有参数,没错当实例化的时候,实例化的过程中就是执行了__init__函数,这个实例化的过程是元类定义的(下章演示)所以

当实例化 A1 = Annie("安妮",17,100) 实际上就是把参数传给了__init__函数,

但是,看__init__函数是位置参数,我们说位置参数是必传的,那么self是什么?,并且为什么能在方法 slogan中调用name呢?

 

self

在实例化的过程当中会把实例本身传给init函数的第一个值

当我们调用实例的数据属性的时候是要 A1.name,那么函数slogan想要调用,实例的数据属性是不会就要把实例传入这个函数中,然后

通过传入的实例去调用实例的数据属性。就像下面的例子中把slogan函数拿了出来,然后把实例传入,这样才能调用name属性,所以self

就是替我们做了把实例传入到函数的操作。

 1 class Annie:
 2     camp = "Noxus"
 3     def __init__(self,name,attack,life):
 4         self.name = name
 5         self.attack = attack
 6         self.life = life
 7 
 8 def slogan(func):
 9     print(func.name)
10 
11 A1 = Annie("安妮",17,100)
12 slogan(A1)
self

 

属性引用

对于一个实例来说,只有一种功能就是:属性引用,上面提到的类中有数据属性和函数属性,包括__init__也是函数属性,并且对象/实例

本身只有数据属性,有人说不对啊实例可以引用类的函数属性啊,其实原理是这样的,本来对实例是无法使用函数属性的,但是python的

class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,下面例子中就可以清楚的看到 A1.slogan,是绑定到Annie.slogan

上的。

 1 class Annie:
 2     camp = "Noxus"
 3     def __init__(self,name,attack,life):
 4         self.name = name
 5         self.attack = attack
 6         self.life = life
 7 
 8     def slogan(func):
 9         print(func.name)
10 
11 A1 = Annie("安妮",17,100)
12 print(Annie.slogan)
13 print(A1.slogan)
14 
15 """
16 输出结果
17 <function Annie.slogan at 0x0060DA08>
18 <bound method Annie.slogan of <__main__.Annie object at 0x00B112D0>>
19 """
方法绑定

 

对象之间的交互

可以通过把对象传给函数来进行对象之间的交互,下面例子中增加了attacks方法,并且这个方法接收一个值,这个值可是是对象,所以把

被攻击的对象传入进去,然后让被攻击对象的生命值减去攻击对象的攻击力,就完成了一次对象之间的交互

 1 class Galen:
 2     camp = "Demacia"
 3     def __init__(self,name,attack,life):
 4         self.name = name
 5         self.attack = attack
 6         self.life = life
 7     def slogan(self):
 8         print("%s喊了口号:德玛西亚!"%self.name)
 9 
10     def attacks(self,enemy):
11         enemy.life-=self.attack
12 
13 class Annie:
14     camp = "Noxus"
15     def __init__(self,name,attack,life):
16         self.name = name
17         self.attack = attack
18         self.life = life
19     def slogan(self):
20         print("%s喊了口号:你是我的小熊么?"%self.name)
21 
22     def attacks(self,enemy):
23         enemy.life-=self.attack
24         
25 #实例化一个盖伦
26 G1 = Galen("盖伦",20,100)
27 #实例化一个安妮
28 A1 = Annie("安妮",17,100) #Annie.__init__(A1,"安妮",17,100)
29 
30 #盖伦攻击安妮
31 G1.slogan()
32 G1.attacks(A1)
33 print(A1.life)
34 #安妮攻击盖伦
35 A1.slogan()
36 A1.attacks(G1)
37 print(G1.life)
对象之间的交互

 

类的属性

就像列表字典一样,类也有一些方法,一些属性,有些默认的方法来自type这个元类中,并且我们在类中定义的数据属性和方法属性也会

保存到 类名.__dict__中。这里面是个字典,有着对于的关系。

 1 class Galen:
 2     """
 3     这是盖伦的类
 4     """
 5     camp = "Demacia"
 6     def __init__(self,name,attack,life):
 7         self.name = name
 8         self.attack = attack
 9         self.life = life
10     def slogan(self):
11         """
12         喊口号的方法
13         :return:空
14         """
15         print("%s喊了口号:德玛西亚!"%self.name)
16 
17     def attacks(self,enemy):
18         enemy.life-=self.attack
19 
20 print(Galen.__dict__)
21 print(Galen.__name__)#类名
22 print(Galen.__doc__)# 类的文档字符串
23 print(Galen.__base__)# 类的第一个父类(继承时会讲)
24 print(Galen.__bases__)# 类所有父类构成的元组(继承时会讲)
25 print(Galen.__dict__)# 类的字典属性
26 print(Galen.__module__)# 类定义所在的模块
27 print(Galen.__class__)# 实例对应的类(仅新式类中)
类的属性方法

 

类与实例(对象)的名称空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性,而类有两种属性:数据属性和函数属性

其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的,可以看到,下面的代码中看到,属性ID是一样的,函数属性

的ID是不一样的。

class Galen:
    """
    这是盖伦的类
    """
    camp = "Demacia"
    def __init__(self,name,attack,life):
        self.name = name
        self.attack = attack
        self.life = life
    def slogan(self):
        """
        喊口号的方法
        :return:空
        """
        print("%s喊了口号:德玛西亚!"%self.name)

G1 = Galen("盖伦",20,100)
#类的数据属性
print(id(Galen.camp))
print(id(G1.camp))
#类的数据属性
print(id(Galen.slogan))
print(id(G1.slogan))
名称空间

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

实例要找到camp,首先会在对象自己的名称空间找,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常  

 

三、类的三大特性之封装 

封装从字面意义上理解的是把东西封起来,在类中要把封装看的更加抽象,封装具体分为数据封装方法封装,就像上面的例子一样,

假如安妮要刺杀盖伦,那能让盖伦看到安妮的阵营么?答案是肯定不行的,所以封装不仅仅是隐藏,就比如说,百度地图,提供给我

们一个接口,让我们调用百度地图的定位功能,那么我们需要关系百度地图是怎么实现定位的么?肯定是不需要。

所以!封装数据 是为了保护隐私  封装方法 隔离复杂度

 

封装分为两个层面

1、就是上面讲到的名称空间

2、就是用下划线的命名方式把属性隐藏起来

封装数据

数据封装在下面例子中可以看出,首先把 Galen的数据属性camp封装起来,所以print(Galen.__camp)是调用不到的,然后name属性也

封装了起来,那么通过print(G1.__money)也是访问不到的,但是在类的内部是可以调用money属性的,所以用inquire_mouney来输出

盖伦有多少钱,那么对于用户来说,调用这个方法就可以得到钱,所以用户不需要关系这个钱是怎么来的,这个钱是否要进行大量的运算,

也不会知道这个钱是对象的数据属性,对于用户来说只知道用这个接口就可以得到钱。

 1 #!/usr/bin/env python
 2 
 3 class Galen:
 4     __camp = "Demacia"
 5     print(__camp)
 6     
 7     def __init__(self,name,money):
 8         self.name = name
 9         self.__money = money
10 
11     def inquire_mouney(self):
12         print(self.__money)
13 
14 G1 = Galen("盖伦",1000)
15 
16 print(G1.name)
17 G1.inquire_mouney()
18 
19 #这样肯定是访问不到的
20 #print(Galen.__camp)
21 #print(G1.__money)
数据封装

方法封装

在方法封装中我们把__conversion_mouney方法封装了起来,它是把人民币换算成韩元,我们发现在外面也是调用不到这个方法的,但是

类的内部是可以调用的。

#!/usr/bin/env python

class Galen:
    __camp = "Demacia"
    print(__camp)

    def __init__(self,name,money):
        self.name = name
        self.__money = money

    def __conversion_mouney(self):
        self.__money = self.__money*163

    def inquire_mouney(self):
        self.__conversion_mouney()
        print(self.__money,"韩元")

G1 = Galen("盖伦",1000)
G1.inquire_mouney()

#这样肯定是访问不到的
# print(Galen.__conversion_mouney)
# print(G1.__conversion_mouney)
方法封装

 

封装剖析

封装,在python中其实就是把被封装的对象做了一个变形,把对象的数据属性 __money 变成了 _Galen__money ,把函数方法

__conversion_mouney变成了_Galen__conversion_mouney,把类的数据属性__camp变成了_Galen__camp,所以正常的去调用

被封装的属性和方法是无法调用的,可以使用变形后的名称调用,当然这种调用方法知道就好了,不要这么用

还有这种变形的操作只在函数定义的时候会变形,你以后添加 __xxx 是不会变形的!

 1 #!/usr/bin/env python
 2 
 3 class Galen:
 4     __camp = "Demacia"
 5     def __init__(self,name,money):
 6         self.name = name
 7         self.__money = money
 8 
 9     def __conversion_mouney(self):
10         self.__money = self.__money*163
11 
12     def inquire_mouney(self):
13         self.__conversion_mouney()
14         print(self.__money,"韩元")
15 
16 G1 = Galen("盖伦",1000)
17 
18 print(G1._Galen__money)
19 print(Galen._Galen__camp)
20 
21 print(Galen.__dict__)
22 print(G1.__dict__)
封装剖析

 

四、类的三大特性之继承与派生

继承,从字面上理解就是谁继承谁,就像linux中的目录权限一样,子目录继承了父目录的目录权限,所以继承是描述了基类的属性如何

“遗传”给派生类。一个子类可以继承它的基类的任何属性,不管是数据属性还是方法。比如二哈是哈士奇类,泰迪是泰迪类,它们都是狗

类,那么狗是狗类,猪是猪类,我们上升一个高度来看它们都属于动物类,所以继承中的父类(也叫基类)有的是不同子类共有的属性和

方法,比如猪与狗都会吃喝拉撒。

继承的语法:很简单class 子类名称(父类名称) ,并且可以通过子类实例化的对象来调用父类的方法与属性,所以继承的最大的好处就是子类

继承了父类的全部功能

 1 #!/usr/bin/env python
 2 
 3 class Demacia:
 4     def monthly_pay(self):
 5         print("我的月薪是10000元")
 6 
 7 class Galen(Demacia):
 8     def slogan(self):
 9         print("德玛西亚!")
10 
11 G1 = Galen()
12 
13 G1.slogan()
14 G1.monthly_pay()
继承

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

 

Super

在上面的例子中父类并没有构造函数,有兴趣的小伙伴可以试试,如果在父类加入了构造函数后,再次运行就会报错,所以在初始化的时候子类

的构造方法必须调用父类的构造方法,来让父类完成初始化的操作。

 1 class Demacia:
 2     def __init__(self,salary):
 3         self.salary = salary
 4     def monthly_pay(self):
 5         print("我的月薪是%s元"%self.salary)
 6 
 7 class Galen(Demacia):
 8     def __init__(self,name,salary):
 9         #增加这么一行,或者 Demacia.__init__(self,salary)
10         super(Galen,self).__init__(salary)
11         self.name = name
12 
13     def slogan(self):
14         print("%s喊了一声:德玛西亚!"%self.name)
15 
16 G1 = Galen("盖伦",10000)
17 
18 G1.slogan()
19 G1.monthly_pay()
super

 

继承实现的原理(继承顺序)

如果一个类,继承了多个类,然后它的父类也继承的其它的类,那么在这些被继承的类中有相同名字的方法时,调用的优先级是有顺序的,在看

继承之前,大家要记住 self 就是对象本身

 

在python中,类如果继承了多个类,那么找寻的方法有两种,分别是:深度优先广度优先,当类是经典类的时候,多继承的情况下,是按照深

度优先的方式查找,当类是新式类的时候会按照广度优先的方式查找,

经典类:先去D找,D没有找B,B没有找A,A没有找C,如果还是未找到,则报错

新式类:先去D找,D没有找B,B没有找C,C没有找A,如果还是未找到,则报错

 

其实python会计算出一个方法解析顺序(MRO)列表,这个列表就是一个简单的所有基类的线性顺序列表,也就说python按照这个列表

的顺序进行查找

 1 class A(object):
 2     def prints(self):
 3         print("A")
 4 
 5 class B(A):
 6     pass
 7     def prints(self):
 8         print("B")
 9 
10 class C(A):
11     def prints(self):
12         print("C")
13 
14 class D(B,C):
15     pass
16     def prints(self):
17         print("D")
18 
19 D().prints()
20 print(D.mro())
继承

 

接口与归一化设计

继承有两种用途

一:继承父类的方法,并且做出自己的改变或者扩展(代码重用)

二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接

口类,并且实现接口中的功能,也就说父类给子类规定了方法中必须有哪些函数。

 

为什么要有接口?其实很简单,就像制作汽车轮胎一样,不管你的轮胎做的什么样,都需要留下与汽车连接的接口,并且这个接口各大汽车轮胎厂

商要是一样的,保证汽车换谁家的轮胎都可以用。

例子中表示 sata和Text两个类都继承Interface这个父类,然后让子类中都有read这个方法。

 1 class Interface():
 2     def read(self):
 3         pass
 4 
 5 class Sata(Interface):
 6     def read(self):
 7         print("read Sata")
 8 
 9 class Text(Interface):
10     def read(self):
11         print("read Text")
12 
13 s = Sata()
14 s.read()
接口与归一化

但是上面代码中中发现接口类并不能限定子类必须实现什么功能,这是因为在python中没有接口的概念,所以想要实现这种接口,可以借助第三方

模块,其次可以用抽象类模拟接口

http://pypi.python.org/pypi/zope.interface

twisted的twisted\internet\interface.py里使用zope.interface

文档https://zopeinterface.readthedocs.io/en/latest/

设计模式:https://github.com/faif/python-patterns

 

抽象类

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能实例化,因为python中没有接口,所以让类继承某个特定的抽象基类,来表示它们支

持抽象基类的接口

抽象了Interface这个基类和抽象read这个方法后,子类 Sata 和 Text就必须有read这个方法,否则会报错

 1 import abc
 2 #接口类使用抽象类
 3 class Interface(metaclass=abc.ABCMeta):
 4     #抽象方法
 5     @abc.abstractclassmethod
 6     def read(self):
 7         pass
 8 
 9 class Sata(Interface):
10     def read(self):
11         print("read Sata")
12 
13 class Text(Interface):
14     def read(self):
15         print("read Text")
16 
17 s = Sata()
18 s.read()
抽象类

 

补充

上面学习了封装与继承,在以下的代码中B的类集成了A的类,并且实例化的对象b执行了 b.test这个方法,那么最终输出的 func这个方法是 B.func

原因是,先找test方法,在B类中没有找到就找到了A类中,A类中找到了test方法,然后就执行了A中的test,A中的test又调用了func的方法,所以

python又重B类找,在B类中找到了func的方法,就执行了。

 1 class A():
 2     def test(self):
 3         print("A.test")
 4         self.func()
 5     def func(self):
 6         print("A.func")
 7 
 8 class B(A):
 9     def func(self):
10         print("B.func")
11 
12 b = B()
13 b.test()
继承与封装的用处  

  如果,想要执行的是A类中的func,怎么办?可以利用封装,因为封装就是变形,变形后就的方法在B类中就找不到了

 1 class A():
 2     def test(self):
 3         print("A.test")
 4         self.__func()
 5     def __func(self):
 6         print("A.func")
 7 
 8 class B(A):
 9     def __func(self):
10         print("B.func")
11 
12 b = B()
13 b.test()
封装与继承的一个用处

 

五、类的三大特性之多态与多态性

多态的意思就是有多种形态,比如文件这个事物,有文本文件有数据文件等等,就像动物这种事物,有猪类,有狗类等等,在python中的序列类型

就是多态的,因为它有列表,元祖,字符串这几个形态,那么多态与多态性,是不同的概念,多态性是一种特性,就比如python中的len方法,len

方法中可以传入任何序列,python并没有类型的限定。

 

下面的代码中,给func入任何序列都可以输出序列的长度,所以obj这个参数就是体现的多态性,一个入口函数,有多种实现方式,本质的原理就是

obj有同名的方法,相同的方法可能执行的不同的功能,字符串__len__就是统计字符串的长度,列表__len__就是统计列表的长度。

 1 a = "123"
 2 b = [1,2,3]
 3 c = (1,2,3)
 4 
 5 def func(obj):
 6     print(obj.__len__())
 7 
 8 func(a)
 9 func(b)
10 func(c)
多态性

 

多态在类中的体现,首先有个动物的基类,动物的基类里面定义了shout的方法,并要求继承的子类都要实现shout的方法,然后创建了 Dog类和Cat类

并都有shout的方法,然后给予两个类实例化出了dog 和cat 两个实例,到此为止以上就叫做多态一种动物的事物有多种形态(猫,狗的形态),在下面定

义了func的函数,对于使用这来说,只需要传入,猫,狗就可以出发shout的方法,他们不需要关系是怎么实现的,也不需要更改代码,所以下面就是体现

的多态性

 1 import abc
 2 
 3 class Animal(metaclass=abc.ABCMeta):
 4     @abc.abstractclassmethod
 5     def shout(self):
 6         pass
 7 
 8 class Cat(Animal):
 9     def shout(self):
10         print("喵喵")
11 
12 class Dog(Animal):
13     def shout(self):
14         print("汪汪")
15 dog = Dog()
16 cat = Cat()
17 
18 
19 def func(obj):
20     obj.shout()
21 
22 func(dog)
23 func(cat)
多态与多态性

 

多态性的优点

1、增加了程序的灵活性

2、增加了程序的额外可扩展性

就像上面的例子一样,还可以产生动物类的猪形态,驴形态等等,那么使用者都无需更改代码,只需要把实例传入func函数即可

 

六、特性属性方法(property)

property是一种特殊的属性,访问它的时候会执行一段功能(函数)然后返回值

 1 class Square():
 2     def __init__(self,side_len):
 3         self.__side_len = side_len
 4 
 5     #计算正方形的周长
 6     @property
 7     def perimeter(self):
 8         return 4*self.__side_len
 9 
10     #计算正方形的面积
11     @property
12     def area(self):
13         return self.__side_len*self.__side_len
14 
15     #变成数据属性 @property 就等于一下操作
16     #perimeter = property(perimeter)
17     #area = property(area)
18 
19 s = Square(5)
20 #不变形执行的调用方式
21 # print(s.perimeter())
22 #print(s.area())
23 #用调用属性的方式来调用
24 print(s.perimeter)
25 print(s.area)
property

 

上面的代码把函数属性变成了数据属性,那么数据属性可以重新赋值,删除等操作,下面代码实现了删除,重新赋值的操作

 1 class Square():
 2     def __init__(self,side_len):
 3         self.__side_len = side_len
 4 
 5     #读取边长
 6     @property#读取
 7     def perimeter(self):
 8         print("获取")
 9         return self.__side_len
10 
11     @perimeter.setter#更改
12     def perimeter(self,value):
13         print("更改")
14         self.__side_len = value
15 
16     @perimeter.deleter#删除
17     def perimeter(self):
18         print("删除")
19         del self.__side_len
20 
21 s = Square(5)
22 print(s.perimeter)
23 s.perimeter = 111
24 del s.perimeter
properyt更改删除

那么其实上面的用法意义并不大,意义大的是,我们可以利用上面的方法来控制数据属性的赋值,删除等只需要在相应的地方,增加相应的逻辑

 1 class Square():
 2     def __init__(self,side_len):
 3         self.__side_len = side_len
 4 
 5     #读取边长
 6     @property#读取
 7     def perimeter(self):
 8         print("获取")
 9         return self.__side_len
10 
11     @perimeter.setter#更改
12     def perimeter(self,value):
13         if not isinstance(value,int):
14             print("更改失败")
15         else:
16             print("更改完成")
17             self.__side_len = value
18 
19     @perimeter.deleter#删除
20     def perimeter(self):
21         print("删除")
22         del self.__side_len
23 
24 s = Square(5)
25 print(s.perimeter)
26 s.perimeter = "111"
27 s.perimeter = 11
28 print(s.perimeter)
更改判断

 

七、静态方法与类方法

只要是方法就是绑定到实例上的函数,类当中定义的函数都是给实例去用的,但是静态方法类方法是给类用的

 

静态方法(staticmethod)

staticmethod主要是方便外部的函数继承到类中,美化代码结构,重点在不需要类实例化的情况下调用方法,也不需要传入self,并且在特定的

地方可以借助静态方法帮我们实例化,如下的 Date 类,输出年月日

 1 import time
 2 class Foo:
 3     @staticmethod
 4     def spam(x,y,z):
 5         print(x,y,z)
 6 
 7 Foo.spam(1,2,3)
 8 
 9 class Date():
10     def __init__(self,year,mon,day):
11         self.year = year
12         self.mon = mon
13         self.day = day
14 
15     def prints(self):
16         print(self.year,self.mon,self.day)
17 
18     @staticmethod  #等于 now = staticmethod(now)
19     def now():
20         t = time.localtime()
21         return  Date(t.tm_year,t.tm_mon,t.tm_mday)
22 d = Date(2017,1,20)
23 c = Date.now()
24 
25 d.prints()
26 c.prints()
staticmethod

 

类方法(classmethod)

类方法与静态方法用法非常相似,区别就是被类方法装饰的函数,会传入cls,也就是类的本身,谁来调用就把谁传入进去,那么有类的本身就可

以操作类的数据属性和方法属性,下面的代码就是通过classmethod给date重新赋值

 1 import time
 2 class Date():
 3     date = None
 4 
 5     @classmethod  #等于 now = staticmethod(now)
 6     def now(cls):
 7         t = time.localtime()
 8         cls.date = [t.tm_year,t.tm_mon,t.tm_mday]
 9 
10 Date.now()
11 d = Date()
12 print(d.date)
classmethod

classmethod也可以借助它帮我们实例化

 1 class Date():
 2     def __init__(self,year,mon,day):
 3         self.year = year
 4         self.mon = mon
 5         self.day = day
 6 
 7     def prints(self):
 8         print(self.year,self.mon,self.day)
 9 
10     @classmethod  #等于 now = staticmethod(now)
11     def now(cls):
12         t = time.localtime()
13         return  cls(t.tm_year,t.tm_mon,t.tm_mday)
14 
15 d=Date.now()
16 d.prints()
classmethod应用场景

 

八、异常处理

在编程的过程中,经常会出现一些报错,那么为了保证在出错的时候程序继续运行,或者做其它的处理,这时异常处理就极为重要

语法:

  try:

    被监控的代码

  except  捕获的错误类型 as 赋值的变量:

    其他操作

先来看一个关于捕获数据类型异常的代码,下面的例子很简单,把data做int转换,但是data是字符串所以无法转换,所以报错通过 try 和except

可以捕获错误

1 data = "abc"
2 try:
3     int(data)
4 except ValueError as e:
5     print(e)
数据类型错误

 

在上面的例子中可以看出,异常处理的捕获的错误类型是很多的,大概有如下这些

 1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
 2 IOError 输入/输出异常;基本上是无法打开文件
 3 ImportError 无法引入模块或包;基本上是路径问题或名称错误
 4 IndentationError 语法错误(的子类) ;代码没有正确对齐
 5 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
 6 KeyError 试图访问字典里不存在的键
 7 KeyboardInterrupt Ctrl+C被按下
 8 NameError 使用一个还未被赋予对象的变量
 9 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
10 TypeError 传入对象类型与要求的不符合
11 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
12 导致你以为正在访问它
13 ValueError 传入一个调用者不期望的值,即使值的类型是正确的
常用异常
 1 BaseException    所有异常的基类
 2 SystemExit    解释器请求退出
 3 KeyboardInterrupt    用户中断执行(通常是输入^C)
 4 Exception    常规错误的基类
 5 StopIteration    迭代器没有更多的值
 6 GeneratorExit    生成器(generator)发生异常来通知退出
 7 StandardError    所有的内建标准异常的基类
 8 ArithmeticError    所有数值计算错误的基类
 9 FloatingPointError    浮点计算错误
10 OverflowError    数值运算超出最大限制
11 ZeroDivisionError    除(或取模)零 (所有数据类型)
12 AssertionError    断言语句失败
13 AttributeError    对象没有这个属性
14 EOFError    没有内建输入,到达EOF 标记
15 EnvironmentError    操作系统错误的基类
16 IOError    输入/输出操作失败
17 OSError    操作系统错误
18 WindowsError    系统调用失败
19 ImportError    导入模块/对象失败
20 LookupError    无效数据查询的基类
21 IndexError    序列中没有此索引(index)
22 KeyError    映射中没有这个键
23 MemoryError    内存溢出错误(对于Python 解释器不是致命的)
24 NameError    未声明/初始化对象 (没有属性)
25 UnboundLocalError    访问未初始化的本地变量
26 ReferenceError    弱引用(Weak reference)试图访问已经垃圾回收了的对象
27 RuntimeError    一般的运行时错误
28 NotImplementedError    尚未实现的方法
29 SyntaxError    Python 语法错误
30 IndentationError    缩进错误
31 TabError    Tab 和空格混用
32 SystemError    一般的解释器系统错误
33 TypeError    对类型无效的操作
34 ValueError    传入无效的参数
35 UnicodeError    Unicode 相关的错误
36 UnicodeDecodeError    Unicode 解码时的错误
37 UnicodeEncodeError    Unicode 编码时错误
38 UnicodeTranslateError    Unicode 转换时错误
39 Warning    警告的基类
40 DeprecationWarning    关于被弃用的特征的警告
41 FutureWarning    关于构造将来语义会有改变的警告
42 OverflowWarning    旧的关于自动提升为长整型(long)的警告
43 PendingDeprecationWarning    关于特性将会被废弃的警告
44 RuntimeWarning    可疑的运行时行为(runtime behavior)的警告
45 SyntaxWarning    可疑的语法的警告
46 UserWarning    用户代码生成的警告
更多异常

对于上述,只能捕获指定类型的异常,那么出现指定类型外的错误就会报错,所以python中有一种万能异常(Exception)

1 data = "abc"
2 try:
3     int(data)
4     tuple(data)
5 except Exception as e:
6     print(e)
7     print(data)
万能异常

还有一点要注意的是,一旦发生异常代码就会从发生异常的代码位置跳转到 except 中,所以不会继续执行 tuple(data)这行代码

 

异常的其它用法

1、其它异常结构

 1 try:
 2     # 主代码块
 3     pass
 4 except Exception as e:
 5     # 异常时,执行该块
 6     pass
 7 else:
 8     # 主代码块执行完,执行该块
 9     pass
10 finally:
11     # 无论异常与否,最终执行该块
12     pass
其它异常结构

2、主动触发异常

1 try:
2     raise Exception('发生了错误。。。')
3 except Exception as e:
4     print(e)
主动触发异常

3、自定义异常

 1 class MyException(Exception):
 2 
 3     def __init__(self, msg):
 4         self.message = msg
 5 
 6     def __str__(self):
 7         return self.message
 8  
 9 try:
10     raise MyException('我的异常')
11 except MyException as e:
12     print(e)
自定义异常

4、断言

使用assert断言是学习python一个非常好的习惯,断言句语格式及用法很简单。在没完善一个程序之前,我们不知道程序在哪里会出错,与其让

它在运行最崩溃,不如在出现错误条件时就崩溃,这时候就需要assert断言的帮助。

1 a = 2
2 assert 1==1
3 assert 2+2==2*2
4 assert range(4)==[0,1,2,3]
5 assert a == 3
断言

 

 

 

 

 

作者北京小远
出处http://www.cnblogs.com/bj-xy/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2017-03-21 18:03  北京-小远  阅读(838)  评论(0编辑  收藏  举报