Python数据分析与实践- 课件 第四章 类与对象_第1页
Python数据分析与实践- 课件 第四章 类与对象_第2页
Python数据分析与实践- 课件 第四章 类与对象_第3页
Python数据分析与实践- 课件 第四章 类与对象_第4页
Python数据分析与实践- 课件 第四章 类与对象_第5页
已阅读5页,还剩104页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Python数据分析与实践12023/10/28第4章类与对象本章学习目标:•深刻理解Python中类、对象的概念,掌握它们的构造和使用•熟练掌握Python面向对象的构造函数和析构函数,以及运算符的重载•理解Python类的继承和组合•熟练掌握Python异常处理机制和内置异常类•熟练掌握Python自定义异常的方法32023/10/284.1面向对象

面向对象编程的英文全称为ObjectOrientedProgramming,简称OOP,它是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。42023/10/284.1.1面向对象编程

面向对象编程的优点是易维护、易复用、易扩展,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。

在Python中所有数据类型都可以视为对象,当然用户也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。52023/10/284.1.1面向对象编程面向对象示例:Student这种数据类型应该被视为一个类,这个类拥有name和score两个属性(Property)。如果要打印一个学生的成绩,首先必须创建出这个学生类对应的对象,然后给对象发一个print_score消息,让对象自己把自己的数据打印出来。具体如下:

classStudent(object):def__init__(self,name,score):=nameself.score=scoredefprint_score(self):print("%s:%s"%(,self.score))62023/10/284.1.1面向对象编程给对象发消息实际上就是调用对象对应的关联函数,一般称之为对象的方法(Method)。面向对象的程序写出来就像这样:>>>bart=Student("BartSimpson",59)l>>>isa=Student("LisaSimpson",87)>>>bart.print_score()>>>lisa.print_score()72023/10/284.1.1面向对象编程

面向对象类(Class)和实例(Instance)的设计思想是从自然界中来的。Class是一种抽象概念,比如这里定义的Student是指学生这个概念,而实例(Instance)是一个个具体的Student,比如BartSimpson和LisaSimpson是两个具体的Student。

所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。面向对象的抽象程度要比函数高,因为一个Class既包含数据又包含操作数据的方法。82023/10/284.1.2类的抽象与封装

对象包含数据以及操作这些数据的代码,一个对象包含的所有数据和代码可以通过类构成一个用户定义的数据类型。事实上,对象就是类类型(classtype)的变量,一旦定义了一个类,就可以创建这个类的多个对象,每个对象与一组数据相关,而这组数据的类型在类中定义。因此,一个类就是具有相同类型的对象的抽象,例如杧果、苹果和橘子都是fruit类的对象。类是用户定义的数据类型,但是在一个程序设计语言中,它和内建的数据类型行为相同。比如创建一个类对象的语法和创建一个整数对象的语法一模一样。92023/10/284.1.2类的抽象与封装

把数据和函数装在一个单独的单元(称为类)的行为称为封装。数据封装是类最典型的特点。数据不能被外界访问,只能被封装在同一个类中的函数访问。这些函数提供了对象数据和程序之间的接口。避免数据被程序直接访问的概念被称为“数据隐藏”。102023/10/284.1.2类的抽象与封装

抽象指仅表现核心的特性而不描述背景细节的行为。类使用了抽象的概念,并且被定义为一系列抽象的属性,例如尺寸、重量和价格,以及操作这些属性的函数。类封装了将要被创建的对象的所有核心属性。因为类使用了数据抽象的概念,所以它们被称为抽象数据类型(ADT)。112023/10/284.1.2类的抽象与封装

封装机制将数据和代码捆绑到一起,避免了外界的干扰和不确定性。它同样允许创建对象。简单地说,一个对象就是一个封装了数据和操作这些数据的代码的逻辑实体。122023/10/284.1.2类的抽象与封装

在一个对象内部,某些代码和(或)某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外地改变或错误地使用了对象的私有部分。132023/10/284.1.2类的抽象与封装

简而言之,类封装了一系列方法,并且可以通过一定的规则约定方法访问权限。在Python中没有public、protected、private之类的访问权限控制修饰词,Python通过方法名约定访问权限。例如:(1)普通名字,表示public。(2)以_前导的名字,从语法上视为public,但约定俗称的意思是“可以被访问,但请视为private,不要随意访问”。(3)以__前导、以__后缀的名字,特殊属性,表示public。(4)以__前导、不以__后缀的名字,表示private。142023/10/284.1.2类的抽象与封装

private名字不能被继承类引用。private不允许通过实例对象直接访问,本质上是因为private属性名被Python解释器改成类名属性名了,因此仍然可以通过类名属性名访问private属性,但是不同版本的Python解释器改造的规则不一致,通常不建议用户这样访问private属性,因为代码不具有可移植性。152023/10/284.1.1类的定义与创建

类(Class)可以将它或多或少地看作是类别或者种类的同义词。在Python中类用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。使用类几乎可以模拟任何东西。162023/10/284.1.1类的定义与创建类是一个用户定义类型,与其他大多数计算机语言一样,Python使用关键字class来定义类。函数的语法格式如下:classclassname:<statement-1>...<statement-n><statement-1>与<statement-n>内可以包含任何有效的Python语句,用来定义类的属性与方法。4.1.1类的定义与创建下面创建一个简单的类Cat类。classCat():#一次模拟小猫的简单尝试def__init__(self,name,age):#初始化属性name和ageS=nameSelf.age=agedefsit(self):#模拟小猫被命令下蹲print(.title()+“isnowsitting.”)通过如上代码,就可以创建一个简单类Cat,赋予了每只小猫下蹲(sit())的能力。4.2.1类的定义与创建在上一节的案例的代码中,已经使用到了一个类中的特殊方法:__init__(self,...)方法,这个方法被称为构造函数,用来初始化对象(实例),在创建新对象时调用。构造函数属于每个对象,每个对象都有自己的构造函数。如果用户未设计构造函数,Python将提供一个默认的构造函数。__init__方法在类的一个对象(实例)被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化。注意,这个名称的开始和结尾都是双下划线。构造函数的作用有个:一是在内存中为类创建一个对象;二是调用类的初始化方法来初始化对象。4.2.1类的定义与创建

__init__()方法定义成包含3个形参,即self、name、age,形参self必不可少,还必须位于其他形参的前面。那么为什么必须在方法定义中包含形参self呢?这是因为Python在调用这个__init__()方法创建Cat实例时将自动传入实参self。每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的性和方法。在创建Cat实例时,Python将调用Cat类的方法__init__()。程序将通过实参向Cat()传递名字和年龄;self会自动传递,因此不需要手动去传递它。每当根据Cat类创建实例时都只需要给最后两个形参(name和age)提供值。4.2.1类的定义与创建

接下来创建一个表示特定小猫的实例。首先可以将类视作有关如何创建实例的说明,即可以理解成Cat类是一系列说明,让Python知道如何创建一个表示特定小猫的实例。my_cat=Cat("tommy",3)print("mycat”snameis"+my_.title()+".")print("mycatis"+str(my_cat.age)+"yearsold.")4.2.1类的定义与创建

前面创建了一个简单的Cat类,并在方法__init__()中定义了name和age属性。如果要访问实例的属性,可以使用句点表示法。编写了如下代码来访问my_cat的name属性的值:my_

句点表示法在Python中很有用,这种语法演示了Python语句如何来获得属性的值。4.2.2构造函数

示例代码中已经使用到类中的一个特殊方法———__init__(self,...),这个方法被称为构造函数,用来初始化对象(实例),在创建新对象时调用。__init__()方法在类的一个对象(实例)被建立时马上运行。

这个方法可以用来对用户的对象做一些用户希望的初始化。构造函数属于每个对象,每个对象都有自己的构造函数。如果用户未设计构造函数,Python将提供一个默认的构造函数。注意,这个名称的开始和结尾都是双下画线。

构造函数的作用有两个:一是在内存中为类创建一个对象;二是调用类的初始化方法来初始化对象。4.2.2构造函数

类的创建和实例化。classPerson:def__init__(self,name):=namedefsayHi(self):print(“Hello,mynameis”,)p=Person(“python”)p.sayHi()运行结果:Hello,mynameispython4.2.2构造函数

__init__()方法定义为取一个参数name(以及普通的参数self)。在这个__init__()里只是创建一个新的域,也称为name。注意它们是两个不同的变量,尽管它们有相同的名字。点号使用户能够区分它们。最重要的是,没有专门调用__init__()方法,只是在创建一个类的新实例的时候把参数包括在圆括号内跟在类名后面,从而传递给__init__()方法。这是这种方法的重要之处。4.2.1类属性和实例属性

类中的属性分为两种:一是类属性,二是实例属性。类属性是在类中方法之外定义的;实例属性则是在构造函数__init__中定义的,定义时以self为前缀,只能通过对象名访问,上一小节创建的Cat类中的和self.age就是实例属性。为了更好的讲解类属性,将依据上一小节中创建的一个简单类Cat,对其进行类属性的增加和修改来说明。类属性的修改和增加都是通过“类名.属性名”的方式直接进行的。4.3.1类属性和实例属性增加类属性:classCat():#一次模拟小猫的简单尝试#增加类属性Reproduction_way=“taisheng”Song_way=“miaomiao”def_init_(self,name,age):#初始化属性name和ageS=nameSelf.age=agedef_sit_(self):#模拟小猫被命令下蹲print(.title()+“isnowsitting.”)4.3.1类属性和实例属性如上面的代码所示,增加了两个类属性,分别是生育后代的方式“taisheng”,以及叫声“miaomiao”,这是猫类的共同属性。4.3.2公有属性和私有属性

Python并不直接支持私有方式,而是靠程序员自己把握在外部进行特性修改的时机。毕竟在使用对象前应该知道如何使用。但是,可以用一些小技巧达到私有特性的效果。

如果想要让方法或者特性变为私有的,即从外部无法进行访问,只需要在它的名字前面加上双下划线即可。具体的讲,以__(双下划线)开头的属性是私有属性,否则这个属性就是公有属性。私有属性通过对象名.类名__私有成员名进行访问,不能在类外进行直接访问。4.3.2公有属性和私有属性定义私有属性classSecret():def__unaccessible(self):print(“Sorry,youcannotaccessible...”)defaccessible(self):Print(“Yes,youcanaccessible,andthesecretis...”)Self.__unaccessible()现在,如在这个例子中展示的,__unaccessible是无法从外界进行访问的。4.3.2公有属性和私有属性

但是从类的内部还是能够进行访问的(比如从accessible()进行访问):s=Secret()s.__unaccessible()运行结果:Yes,youcanaccessible,andthesecretis...Sorry,youcannotaccessible...4.3.2公有属性和私有属性

双下画线虽然有些奇怪,但看起来像是其他编程语言中的标准的私有方法。事实上真正发生的事情是不标准的。因为在类的内部定义中,所有以双下画线开始的命名都将会被翻译成前面加单下画线和类名的形式。例如:Secret._Secret__unaccessible运行结果:unboundmethodSecret.__unaccessible4.3.2公有属性和私有属性

总体来说,想要确保其他人不会访问对象的方法和特性是不可能的,但是像这类的“名称变化术”就是他们不应该访问这些方法或者特性的强信号。

如果不想使用这种方法,但是又想让其他对象不能访问内部数据,那么可以使用双下画线。虽然这不过是一个习惯,但的确有实际效果。例如,前面有下画线的名字都不会被带星号的import语句“formmoduleimport*”导入。

有些编程语言支持多种层次的成员变量或特性私有化,比如在Java中就支持4种级别。尽管单、双下画线在某种程度上给出了两个级别的私有性,但是Python并没有真正的私有化支持。4.4.1调用类的方法

在这里,仍旧使用4.2.1小节中创建的Cat类实例。在创建Cat类实例后,就可以使用句点表示法来调用Cat类中定义的任何方法。

如果想要调用类中方法,可通过指定实例的名称(这里是my_cat)和想要调用的方法,并用句点分隔它们。遇到代码my_cat._sit_()时,Python在类Cat中查找方法sit()并运行其代码块。classCat():==snip==my_cat=Cat(“tommy”,3)my_cat._sit_()4.4.2类的方法分类

在Python中类的方法大致上可以分为三类:类方法、实例方法和静态方法。

类方法,是类对象所拥有的方法,需要用修饰器“@classmethod”来标识其为类方法。它能够通过实例对象和类对象去访问。类方法的用途就是可以对类属性进行修改。对于类方法,第一个参数必须是类对象,一般以“cls”作为第一个参数。举例如下,classpeople:country="china"@classmethoddefgetCountry(cls):#类方法returncls.country4.4.2类的方法分类

实例方法,在类中最常定义的成员方法,它至少有一个参数并且必须以实例对象作为其第一个参数,一般以名为’self’的变量作为第一个参数。(注意:不能通过类对象引用实例方法)。举例如下,@classmethoddefsetCountry(cls,country):#类方法cls.country=countryclassInstanceMethod(object):def__init__(self,a):self.a=adeff1(self):print(“Thisis{0}”.format(self))deff2(self,a):print(“Value:{0}”.format(a))4.4.2类的方法分类

静态方法,需要通过修饰器”@staticmethod”来进行修饰,静态方法对参数没有要求,不需要多定义参数。在静态方法中只能访问属于类的成员,不能访问属于对象的成员,而静态方法也只能通过类名调用。举例如下,country=“china”@staticmethoddefgetcountry():returnpeople.country@staticmethoddefsetcountry(countryName):people.country=countryName4.4.2类的方法分类

对于这三种不同的方法,就出现了一个问题,既然有了实例方法,类方法和静态方法与之相比又有什么好处呢?具体的将,在类方法中,不管是使用实例还是类调用方法,都会把类作为第一个参数传递进来,这个参数就是类本身。如果继承了这个使用类方法的类,该类的所有子类都会拥有了这个方法,并且这个方法会自动指向子类本身。静态方法是和类与实例都没有关系的,完全可以使用一般方法代替,但是使用静态方法可以更好的组织代码,防止代码变大后变得比较混乱。类方法是可以替代静态方法的。静态方法不能在继承中修改。4.4.3析构函数

Python中没有专用的构造和析构函数,但是一般可以在__init__和__del__分别完成初始化和删除操作,可用这个替代构造和析构。从这个意义上讲,__init__方法:属于python语言的构造函数;__del__方法:属于python语言的析构函数,它在对象消逝的时候被调用,用来释放对象占用的资源。析构函数在对象就要被垃圾回收之前调用,但发生调用的具体时间是不可知的。4.3.3析构函数将通过一个例子来说明Python的析构函数,举例如下,classtest():def__init__(self):print(“AAA”)def__del__(self):print(“BBB”)defmy(self):print(“CCC”)>>>obj=test()AAABBB>>>obj.my()CCC>>>delobjBBB4.3.3析构函数

上述例子中的__del__函数就是一个析构函数了,当使用del删除对象时,会调用他本身的析构函数,另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。__del__()也是可选的,如果不提供,则Python会在后台提供默认析构函数如果要显式的调用析构函数,可以使用del关键字,方式如下:

del对象名412023/10/284.5类的继承

代码重用是软件工程的重要目标之一,类的重用是面向对象的核心内容之一在编写类时,并非总是要从新开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承,在这个现成类的基础上创建新类,在所创建的新的类中通过添加代码,来扩展现成类的属性和方法。这样不仅能够减少工作量,而且能降低出现错误的可能性。422023/10/284.5.1父类与子类

一个类继承另一个类时,它将自动获得另一个类的所有属性和方法,原有的类称为基类、父类或超类(Baseclass、Superclass),而新类称为子类(Subclass)。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。

在继承关系中,继承者是被继承者的子类。子类继承所有祖先的非私有属性和非私有方法,子类也可以增加的属性和方法,子类还可以通过重定义覆盖从父类中继承而来的方法。432023/10/284.5.2继承的语法具体的继承语法,通过一个实例来进行展示说明,具体如下。例如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印,classAnimal(object):defrun(self):print(“Animalisrunning...”)442023/10/284.5.2继承的语法当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:classDog(Animal):passclassCat(Animal):pass

此时,对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。创建子类时,父类必须包含在当前文件中,且位于子类前面。我们定义了子类Dog和Cat。定义子类时,必须在括号内指定父类的名称。452023/10/284.5.2继承的语法

继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:dog=Dog()dog.run()cat=Cat()cat.run()运行结果如下:Animalisrunning...Animalisrunning...462023/10/284.5.2继承的语法

继承的第二个好处需要用户对代码做一点改进。大家可以看到,无论是Dog还是Cat,它们在run()的时候显示的都是Animalisrunning...,符合逻辑的做法是分别显示Dogisrunning...和Catisrunning...,因此对Dog和Cat类改进如下:classDog(Animal):defrun(self):print("Dogisrunning...")classCat(Animal):defrun(self):print("Catisrunning...")运行结果如下:Dogisrunning...Catisrunning...472023/10/284.5.2继承的语法

当子类和父类存在相同的run()方法时,子类的run()覆盖父类的run(),在代码运行时总是会调用子类的run()。当然,用户也可以给子类增加一些方法,比如Dog类:classDog(Animal):defrun(self):print("Dogisrunning...")defeat(self):print("Eatingmeat...")482023/10/284.5.3多重继承

继承是面向对象编程的一个重要的方式,因为通过继承,子类就可以扩展父类的功能。以一个例子来进行讲解。492023/10/284.5.3多重继承

这里想一下Animal类层次的设计,假设要实现Dog(狗)、Bat(蝙蝠)、Parrot(鹦鹉)、Ostrich(鸵鸟)4种动物,如果按照哺乳动物和鸟类归类,可以设计出如下类层次。•哺乳类:能跑的哺乳类,能飞的哺乳类。•鸟类:能跑的鸟类,能飞的鸟类。如果要再增加“宠物类”和“非宠物类”,那么类的数量会呈指数增长,很明显这样设计是不行的,正确的做法是采用多重继承。首先,主要的类层次仍按照哺乳类和鸟类设计。502023/10/284.5.3多重继承classAnimal(object):pass#大类classMammal(Animal):passclassBird(Animal):pass#各种动物classDog(Mammal):passclassBat(Mammal):passclassParrot(Bird):passclassOstrich(Bird):pass512023/10/284.5.3多重继承

现在,要给动物再加上Runnable和Flyable的功能,只需要先定义好Runnable和Flyable的类:classRunnable(object):defrun(self):print(“Running...”)classFlyable(object):deffly(self):print(“Flying...”)522023/10/284.5.3多重继承对于需要Runnable功能的动物,就多继承一个Runnable,例如Dog:classDog(Mammal,Runnable):pass532023/10/284.5.3多重继承对于需要Flyable功能的动物,就多继承一个Flyable,例如Bat:classBat(Mammal,Flyable):pass通过多重继承,一个子类就可以同时获得多个父类的所有功能。542023/10/284.5.4运算符的重载

在Python类中可以重写某些运算符的方法函数,例如类中提供了__add__()这个钩子函数,当调用“+”(加法)运算时,实际上是调用了__add__()钩子函数,用户在类中可以重写这些钩子函数。552023/10/284.5.4运算符的重载

在Python中带有前/后缀、双下画线的方法函数称为钩子函数,钩子函数具有以下特征:(1)多数钩子函数均可在类中被重写。(2)钩子函数无预设值。(3)相应运算符调用时会自动映射调用这些钩子函数。562023/10/284.5.4运算符的重载下面表例举一些常见运算符重载方法:methodoverloadcall__init__构造函数对象创建:X=Class(args)__del__析构函数X对象收回__add__运算法+X+Y,X+=Y__or__运算符|X|Y,X|=Y_repr__,__str__打印,转换print(X),repr(X),str(X)__call__函数调用X(*args,**kwargs)__getattr__点号运算X.undefined__setattr__属性赋值语句X.any=value572023/10/284.5.4运算符的重载__delattr__属性删除delX.any__getattribute__属性获取X.any__getitem__索引运算X[key],X[i:j]__setitem__索引赋值语句X[key],X[i:j]=sequence__delitem__索引和分片删除delX[key],delX[i:j]__len__长度len(X)__bool__布尔测试bool(X)__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比较X<Y,X>Y,X<=Y,X>=Y,X==Y,X!=Y582023/10/284.5.4运算符的重载__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比较X<Y,X>Y,X<=Y,X>=Y,X==Y,X!=Y__radd__右侧加法other+X__iadd__实地(增强的)加法X+=Y(orelse__add__)__iter__,__next__迭代环境I=iter(X),next()__contains__成员关系测试iteminX(任何可迭代)__index__整数值hex(X),bin(X),oct(X)__enter__,__exit__环境管理器withobjasvar:__get__,__set__,__delete__描述符属性X.attr,X.attr=value,delX.attr__new__创建在__init__之前创建对象592023/10/284.6类的组合

前面讲了面向类与对象的继承,知道了继承是一种什么“是”什么的关系。然而类与类之间还有另一种关系,这就是组合。这是类的另一种重用的方式,如果程序中的类需要使用一个其他对象,就可以使用类的组合方式。在Python中,一个类可以包含其他类的对象作为属性,这就是类的组合。

先来看两个例子:先定义两个类,一个老师类,老师类有名字,年龄,出生的年,月和日,所教的课程等特征以及走路,教书的技能:602023/10/284.6类的组合classTeacher:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalkingslowly”%)defteach(self):print(“%sisteaching”%)612023/10/284.6类的组合

再定义一个学生类,学生类有名字,年龄,出生的年,月和日,学习的组名等特征以及走路,学习的技能:622023/10/284.6类的组合classStudent:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalkingslowly”%)defstudy(self):print(“%sisstudying”%)632023/10/284.6类的组合

根据类的继承这个特性,可以把代码缩减一下。定义一个人类,然后再让老师类和学生类继承人类的特征和技能:classPeople:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.year=yearself.mon=monself.day=daydefwalk(self):print(“%siswalking”%)642023/10/284.6类的组合classTeacher(People):def__init__(self,name,age,year,mon,day,course):People.__init__(self,name,age,year,mon,day)self.course=coursedefteach(self):print(“%sisteaching”%)classStudent(People):def__init__(self,name,age,year,mon,day,group):People.__init__(self,name,age,year,mon,day)self.group=groupdefstudy(self):print(“%sisstudying”%)652023/10/284.6类的组合再对老师和学生进行实例化,得到一个老师和一个学生。t1=Teacher(“alex”,28,1989,9,2,“python”)s1=Student(“jack”,22,1995,2,8,“group2”)662023/10/284.6类的组合

现在想知道t1和s1的名字、年龄、出生的年/月/日都很容易,但是想一次性打印出t1或s1的生日就不那么容易了,这时需要用字符串进行拼接,有没有什么更好的办法呢?

有,那就是组合。继承是一个子类与一个父类的关系,而组合是一个类与另一个类的关系。可以说每个人都有生日,而不能说人是生日,这样就要使用组合的功能。672023/10/284.6类的组合

可以把出生的年/月/日再另外定义一个日期的类,然后用老师或者学生类与这个日期的类组合起来,就可以很容易地得出老师t1或者学生s1的生日,再也不用字符串拼接那么麻烦。请看下面的代码:682023/10/284.6类的组合classDate:def__init__(self,year,mon,day):self.year=yearself.mon=monself.day=daydefbirth_info(self):print(“Thebirthis%s-%s-%s”%(self.year,self.mon,self.day))692023/10/284.6类的组合classPeople:def__init__(self,name,age,year,mon,day):=nameself.age=ageself.birth=Date(year,mon,day)defwalk(self):print(“%siswalking”%)702023/10/284.6类的组合classTeacher(People):def__init__(self,name,age,year,mon,day,course):People.__init__(self,name,age,year,mon,day)self.course=coursedefteach(self):print(“%sisteaching”%)712023/10/284.6类的组合classStudent(People):def__init__(self,name,age,year,mon,day,group):People.__init__(self,name,age,year,mon,day)self.group=groupdefstudy(self):print(“%sisstudying”%)t1=Teacher(“alex",28,1989,9,2,"python”)s1=Student(“jack",22,1995,2,8,"group2”)722023/10/284.6类的组合

这样一来,可以使用跟前面一样的方法来调用老师t1或学生s1的姓名,年龄等特征以及走路,教书或者学习的技能。print()t1.walk()t1.teach()输出为:alexalexiswalkingalexisteaching732023/10/284.6类的组合

那要怎么能够知道他们的生日呢:print(t1.birth)输出为:<__main__.Dateobjectat0x0000000002969550>742023/10/284.6类的组合

这个birth是子类Teacher从父类People继承过来的,而父类People的birth又是与Date这个类组合在一起的,所以,这个birth是一个对象。而在Date类下面有一个birth_info的技能,这样就可以通过调用Date下面的birth_info这个函数属性来知道老师t1的生日了。t1.birth.birth_info()得到的结果为:Thebirthis1989-9-2752023/10/284.6类的组合

组合就是一个类中使用到另一个类,从而把几个类拼到一起。组合的功能也是为了减少重复代码。

在实际的项目开发过程中,如果仅仅是只使用继承和组合中的一种技术,是很难满足实际需求的,所以在实际的开发过程中,开发人员通常会将两种技术结合起来使用。762023/10/284.7类的异常处理异常处理在任何一门编程语言里都是非常被关注的一个话题,良好的异常处理可以让程序更加健壮,清晰的错误信息更能帮助快速修复问题。在Python中,和部分高级语言一样,使用了try/except语句块来处理异常,如果你有其他编程语言的经验,实践起来并不难。772023/10/284.7.1异常

异常即是在程序执行过程中发生的影响程序正常运行的一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。异常处理使程序能够处理完异常后继续它的正常执行,不至于使程序因异常导致退出或崩溃。782023/10/284.7.1异常举一个具体的例子:打开一个不存在的文件。代码如下:fr=open(“/notthere”,”r”)运行结果:Traceback(mostrecentcalllast):File“tiaoshi005.py”,line1,in<module>fr=open(“/notthere”,”r”)FileNotFoundError:[Errno2]Nosuchfileordirectory:‘/notthere’例子中的代码视图打开一个不存在的文件,运行之后,抛FileNotFoundError异常。4.7.2Python中的异常类

Python程序出现异常时将抛出一个异常类的现象。Python中所有的异常类的根类都是BaseException类,他们都是BaseException的直接或间接子类。大部分常规异常类的基类是Exception的子类。下表列出了Python中内置的标准异常。而自定义异常类都是继承自这些标准异常。异常名称描述BaseException所有异常的基类SystemExit解释器请求退出KeyboardInterrupt用户中断执行(通常是输入^C)Exception常规错误的基类StopIteration迭代器没有更多的值4.7.2Python中的异常类GeneratorExit生成器(generator)发生异常来通知退出StandardError所有的内建标准异常的基类ArithmeticError所有数值计算错误的基类FloatingPointError浮点计算错误OverflowError数值运算超出最大限制ZeroDivisionError除(或取模)零(所有数据类型)AssertionError断言语句失败AttributeError对象没有这个属性4.7.2Python中的异常类EOFError没有内建输入,到达EOF标记EnvironmentError操作系统错误的基类IOError输入/输出操作失败OSError操作系统错误WindowsError系统调用失败ImportError导入模块/对象失败LookupError无效数据查询的基类IndexError序列中没有此索引(index)4.7.2Python中的异常类KeyError映射中没有这个键MemoryError内存溢出错误(对于Python解释器不是致命的)NameError未声明/初始化对象(没有属性)UnboundLocalError访问未初始化的本地变量ReferenceError弱引用(Weakreference)试图访问已经垃圾回收了的对象RuntimeError一般的运行时错误NotImplementedError尚未实现的方法SyntaxErrorPython语法错误4.7.2Python中的异常类IndentationError缩进错误TabErrorTab和空格混用SystemError一般的解释器系统错误TypeError对类型无效的操作ValueError传入无效的参数UnicodeErrorUnicode相关的错误UnicodeDecodeErrorUnicode解码时的错误UnicodeEncodeErrorUnicode编码时错误4.7.2Python中的异常类UnicodeTranslateErrorUnicode转换时错误Warning警告的基类DeprecationWarning关于被弃用的特征的警告FutureWarning关于构造将来语义会有改变的警告OverflowWarning旧的关于自动提升为长整型(long)的警告PendingDeprecationWarning关于特性将会被废弃的警告RuntimeWarning可疑的运行时行为(runtimebehavior)的警告SyntaxWarning可疑的语法的警告UserWarning用户代码生成的警告4.7.3捕获与处理异常捕捉异常通常使用try/except语句。

try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果不想在异常发生时结束程序,只需在try里捕获它。4.7.3捕获与处理异常以下为简单的try....except的语法:try:<语句>#运行别的代码except<名字>:<语句>#如果在try部份引发了'name'异常except<名字>,<数据>:<语句>#如果引发了'name'异常,获得附加的数据4.7.3捕获与处理异常

当开始一个try语句后,Python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

如果当try后的语句执行时发生异常,Python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。

如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。如果在try子句执行时没有发生异常,Python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。4.7.3捕获与处理异常当然也可以不带任何异常类型使用except,如下示例:try:正常的操作......................except:发生异常,执行这块代码......................else:如果没有异常执行这块代码以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息,因为它捕获所有的异常。4.7.4自定义异常类

通过创建一个新的异常类,程序可以命名自己的异常。自定义异常应该是通过直接或间接的方式继承自典型的Exception类。4.7.4自定义异常类

以下为与RuntimeError相关的实例,实例中创建了一个类,基类为RuntimeError,用于在异常触发时输出更多的信息。在try语句块中,用户自定义的异常后执行except块语句,变量e是用于创建Networkerror类的实例。classNetworkerror(RuntimeError): def__init__(self,arg): self.args=arg4.7.4自定义异常类在定义以上类后,可以触发该异常,如下所示:try: raiseNetworkerror("Badhostname")except(Networkerror)ase: print(e.args)但是,因为Networkerror是一个自定义类,因此需要使用raise来显式地抛出异常。4.7.4自定义异常类

自定义异常的其他使用方法则与标准模块中的异常类的使用方法一致。下面将通过一个具体的例子来进行自定义异常使用的详细讲解。具体如下:classShortInputException(Exception):#Auser-definedexceptionclass.def__init__(self,length,atleast):Exception.__init__(self)self.length=length self.atleast=atleast4.7.4自定义异常类try:s=raw_input('Entersomething-->')iflen(s)<3:raiseShortInputException(len(s),3)else:print(s)exceptEOFError:print'\nWhydidyoudoanEOFonme?'exceptShortInputExceptionasx:print('ShortInputException:Theinputwaslength%d,\wasexpectingatleast%d.'%(x.length,x.atleast))else:print('Noexceptionwasraised.')4.7.4自定义异常类

在上述例子中,先自定义了一个名为ShortInputException的异常类,其用来判断用户输入的字符串长度是否满足要求。在本例子中,其判断输入的字符串长度是否等于大于3个字符,若不满足,则抛出该异常。4.7.5with语句

有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论