python归一化 增大差异_python-面向对象进阶

python-面向对象进阶

三大特性:继承,多态,封装

1,初识继承

继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题。

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类。

1 #父类/基类/超生类

2 #子类/派生类(继承父类)

3 #_bases_则是查看所有继承的父类

代码示例如下:

1 classParentClass1:2 pass

3 classParentClass2:4 pass

5 classSubClass1(ParentClass1):6 pass

7 classSubClass2(ParentClass1,ParentClass2):8 pass

9

10 print(SubClass1.__bases__)11 print(SubClass2.__bases__)12

13 ###

14 (,)15 (, )

2,继承与抽象(先抽象再继承)

抽象即抽取类似或者说比较像的部分。

抽象分成两个层次:

1.将奥巴马和梅西这俩对象比较像的部分抽取成类;

2.将人,猪,狗这三个类比较像的部分抽取成父类。

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

3, 继承与重用性

继承与重用性

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

1 classHero:2 def __init__(self, nickname, life_value, aggressivity):3 self.nickname =nickname4 self.life_value =life_value5 self.aggressivity =aggressivity6 defattack(self, enemy):7 enemy.life_value -=self.aggressivity8

9 classGailen(Hero):10 pass

11

12 classRiven(Hero):13 pass

14

15 gailen = Gailen('草丛伦', 120, 40)16 print(gailen.nickname, gailen.life_value, gailen.aggressivity) #这里能实现,说明了它是继承了Hero的属性

提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大节省了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大.

再说属性查找

查找顺序:对象自己 → 对象自己的类 → 父类

1 #查找顺序:对象自己 → 对象自己的类 → 父类

2 classFoo:3 deff1(self):4 print('Foo.f1')5

6 deff2(self):7 print('Foo.f2')8 self.f1() #b.f1()

9

10 classBar(Foo):11 deff1(self):12 print('Bar.f1')13

14 b =Bar()15 b.f2()16 ###Foo.f2

17 ###Bar.f1

4,派生

当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

1 classHero:2 def __init__(self, nickname, life_value, aggressivity):3 self.nickname =nickname4 self.life_value =life_value5 self.aggressivity =aggressivity6 defattack(self, enemy):7 enemy.life_value -=self.aggressivity8

9 classRiven(Hero):10 camp='Noxus'

11 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类

12 print('from riven')13 def fly(self): #在自己这里定义新的

14 print('%s is flying' % self.nickname)

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要其传值。

1 classRiven(Hero):2 camp='Noxus'

3 def __init__(self,nickname,aggressivity,life_value,skin):4 Hero.__init__(self,nickname,aggressivity,life_value) #调用父类功能

5 self.skin=skin #新属性

6 def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类

7 Hero.attack(self,enemy) #调用功能

8 print('from riven')9 def fly(self): #在自己这里定义新的

10 print('%s is flying' %self.nickname)11

12 r1=Riven('锐雯雯',57,200,'比基尼')13 r1.fly()14 print(r1.skin)15

16 '''

17 运行结果18 锐雯雯 is flying19 比基尼20

21 '''

5,继承的实现原理

1,方法解析顺序(MRO)列表

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

1 classA(object):2 deftest(self):3 print('from A')4

5 classB(A):6 deftest(self):7 print('from B')8

9 classC(A):10 deftest(self):11 print('from C')12

13 classD(B):14 deftest(self):15 print('from D')16

17 classE(C):18 deftest(self):19 print('from E')20

21 classF(D,E):22 #def test(self):

23 #print('from F')

24 pass

25 f1=F()26 f1.test()27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

28

29 """

30 from D31 (, , , , , , )32 """

2,区别新式类和经典类

1 #在python2中-->经典类:没有继承object的类,以及它的子类都称经典类

2 classFoo:3 pass

4 classBar(Foo):5 pass

6

7 #在python2中-->新式类:继承object的类,以及它的子类都称之为新式类

8 classFoo(object):9 pass

10 classBar(Foo):11 pass

12

13 #在python3中-->新式类:一个类没有继承object类,默认就继承了object

14 class Foo(): #--> class Foo(object):

15 pass

16 print(Foo.__bases__)17

18 ###

19 (,)

3,深度优先和广度优先方式查找

从左边开始就一直走到底,然后后面再这样一直轮下去

新式类不会走到头,快要到头的时候折返回来往另外一个找,最后一个爹一条路走到底

代码示例

1 classA(object):2 deftest(self):3 print('from A')4

5 classB(A):6 deftest(self):7 print('from B')8

9 classC(A):10 deftest(self):11 print('from C')12

13 classD(B):14 deftest(self):15 print('from D')16

17 classE(C):18 deftest(self):19 print('from E')20

21 classF(D,E):22 #def test(self):

23 #print('from F')

24 pass

25 f1=F()26 f1.test()27 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

28

29 #新式类继承顺序:F->D->B->E->C->A

30 #经典类继承顺序:F->D->B->A->E->C

31 #python3中统一都是新式类

32 #pyhon2中才分新式类与经典类

33 '''

34 print(F._mro_)执行结果35

36 137 (, , , , , , )38 '''

4,子类重用父类的方法或属性

在子类派生出的新方法中,往往需要重用父类的方法,我们有两种方式实现

1,法一:指名道姓(不依赖继承)

指名道姓,不依赖继承,即父类名.父类方法()

1 classHero:2 def __init__(self, nickname, life_value, aggressivity):3 self.nickname =nickname4 self.life_value =life_value5 self.aggressivity =aggressivity6 defattack(self, enemy):7 enemy.life_value -=self.aggressivity8

9 classGailen(Hero):10 camp = 'Demacia'

11 defattack(self, enemy):12 Hero.attack(self, enemy) #指名道姓

13 print('from Gailen Class')14

15 classRiven(Hero):16 camp = 'Noxus'

17

18 gailen = Gailen('草丛伦', 100, 30)19 riven = Riven('锐雯雯', 80, 50)20 print(riven.life_value)21 gailen.attack(riven)22 print(riven.life_value)23

24 """

25 8026 from Gailen Class27 5028 """

1 classHero:2 def __init__(self, nickname, life_value, aggressivity):3 self.nickname =nickname4 self.life_value =life_value5 self.aggressivity =aggressivity6 defattack(self, enemy):7 enemy.life_value -=self.aggressivity8

9 classGailen(Hero):10 camp = 'Demacia'

11 def __init__(self, nickname, life_value, aggressivity, weapon):12 #self.nickname = nickname

13 #self.life_value = life_value

14 #self.aggressivity = aggressivity

15 Hero.__init__(self, nickname, life_value, aggressivity)16

17 self.weapon =weapon18 defattack(self, enemy):19 Hero.attack(self, enemy) #指名道姓

20 print('from Gailen Class')21

22 gailen = Gailen('草丛伦', 100, 30, '大宝剑')23 print(gailen.__dict__)24

25 """

26 {'nickname': '草丛伦', 'life_value': 100, 'aggressivity': 30, 'weapon': '大宝剑'}27 """

2,法二:super()(依赖继承)

super(),依赖继承

1 #方式二

2 classHero:3 def __init__(self, nickname, life_value, aggressivity):4 self.nickname =nickname5 self.life_value =life_value6 self.aggressivity =aggressivity7 defattack(self, enemy):8 enemy.life_value -=self.aggressivity9

10 classGailen(Hero):11 camp = 'Demacia'

12 defattack(self, enemy):13 super(Gailen, self).attack(enemy) #依赖继承,super(自己的类名,self)

14 print('from Gailen Class')15

16 classRiven(Hero):17 camp = 'Noxus'

18

19 gailen = Gailen('草丛伦', 100, 30)20 riven = Riven('锐雯雯', 80, 50)21

22 print(riven.life_value)23 gailen.attack(riven)24 print(riven.life_value)25

26 """

27 8028 from Gailen Class29 5030 """

在python3中super()中已经默认传入参数,可以不用传参数

1 classHero:2 def __init__(self, nickname, life_value, aggressivity):3 self.nickname =nickname4 self.life_value =life_value5 self.aggressivity =aggressivity6 defattack(self, enemy):7 enemy.life_value -=self.aggressivity8

9 classGailen(Hero):10 camp = 'Demacia'

11 def __init__(self, nickname, life_value, aggressivity, weapon):12 #self.nickname = nickname

13 #self.life_value = life_value

14 #self.aggressivity = aggressivity

15 super().__init__(nickname, life_value, aggressivity) #在python3中默认可以不用在super中写入参数

16

17 self.weapon =weapon18 defattack(self, enemy):19 Hero.attack(self, enemy) #指名道姓

20 print('from Gailen Class')21

22 gailen = Gailen('草丛伦', 100, 30, '大宝剑')23 print(gailen.__dict__)24

25 """

26 {'nickname': '草丛伦', 'life_value': 100, 'aggressivity': 30, 'weapon': '大宝剑'}27 """

,super()依赖mro列表查找

这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super仍然会按照mro继续往后查找。

1 #A没有继承B,但是A内super会基于C.mro()继续往后找

2 classA:3 deff1(self):4 print('from A')5 super().f1()6 classB:7 deff1(self):8 print('from B')9 classC(A,B):10 pass

11

12 print(C.mro())13 c =C()14 c.f1() #打印结果:from B

15

16 """

17 [, , , ]18 from A19 from B20 """

6,组合(类的组合)

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

1,组合与重用性

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同。

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,这时用组合比较好。

代码示例

1 classPeople:2 def __init__(self, name, age, sex):3 self.name =name4 self.age =age5 self.sex =sex6

7 classTeacher(People):8 def __init__(self, name, age, sex, level, salary):9 super().__init__(name, age, sex)10 self.level =level11 self.salary =salary12

13 classStudent(People):14 def __init__(self, name, age, sex, class_time):15 super().__init__(name, age, sex)16 self.class_time =class_time17

18 classCourse:19 def __init__(self, course_name, course_price, course_period):20 self.course_name =course_name21 self.course_price =course_price22 self.course_period =course_period23 deftell_info(self):24 print('课程名为 课程价钱为 课程周期为' %(self.course_name, self.course_price, self.course_period))25

26 classDate:27 def __init__(self, year, mon, day):28 self.year =year29 self.mon =mon30 self.day =day31 deftell_info(self):32 print('I was born in %s年%s月%s日' %(self.year, self.mon, self.day))33

34 teacher1 = Teacher('luoma', 28, 'male', 10, 3000)35 teacher2 = Teacher('laoluo', 38, 'male', 30, 3000)36 python = Course('python', 3000, '3mons')37 linux = Course('linux', 2000, '4mons')38

39 teacher1.course =python40 teacher2.course =python41

42

43 print(python)44 print(teacher1.course) #teacher1.course == python

45 print(teacher2.course)46 print(teacher1.course.course_name) #teacher1.course.course_name == Course.course_name

47

48 teacher1.course.tell_info()49

50 student1 = Student('laoqiu', 18, 'male', 8)51

52 #给老师这个对象定制了课程属性,让这个属性指向另一个对象,将老师类和课程类组合到一起

53 student1.course1 =python54 student1.course2 =linux55 student1.course1.tell_info()56 student1.course2.tell_info()57

58 student1.courses =[]59 student1.courses.append(python)60 student1.courses.append(linux)61 print(student1.courses)62

63 #创建一个学生对象和一个时间类,可以达到这种效果

64 d = Date(1999, 4, 20)65 student1.birth =d66 print(student1.birth.tell_info())67 print(student1.course1.tell_info())68

69 """

70 <__main__.course object at>71 <__main__.course object at>72 <__main__.course object at>73 python74 课程名为 课程价钱为<3000> 课程周期为<3mons>75 课程名为 课程价钱为<3000> 课程周期为<3mons>76 课程名为 课程价钱为<2000> 课程周期为<4mons>77 [<__main__.course object at>, <__main__.course object at>]78 I was born in 1999年4月20日79 None80 课程名为 课程价钱为<3000> 课程周期为<3mons>81 None82 """

7,抽象类与归一化设计

1,什么是接口

你好,给我开个查询接口>>>此时的接口指的是:自己提供给使用者来调用自己功能的方式\方法\入口,java中的interface使用如下

java中的interface:

1 =================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java2 /*

3 *Java的Interface接口的特征:4 * 1)是一组功能的集合,而不是一个功能5 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作6 * 3)接口只定义函数,但不涉及函数实现7 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */

8

9 package com.oo.demo;10 public interface IAnimal {11 public void eat();12 public void run();13 public void sleep();14 public void speak();15 }16

17 =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口18 package com.oo.demo;19 public class Pig implements IAnimal{ //如下每个函数都需要详细实现20 public void eat(){21 System.out.println("Pig like to eat grass");22 }23

24 public void run(){25 System.out.println("Pig run: front legs, back legs");26 }27

28 public void sleep(){29 System.out.println("Pig sleep 16 hours every day");30 }31

32 public void speak(){33 System.out.println("Pig can not speak"); }34 }35

36 =================第三部分:Person2.java37 /*

38 *实现了IAnimal的“人”,有几点说明一下:39 * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,
但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样40 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,
这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */

41

42 package com.oo.demo;43 public classPerson2 implements IAnimal {44 public void eat(){45 System.out.println("Person like to eat meat");46 }47

48 public void run(){49 System.out.println("Person run: left leg, right leg");50 }51

52 public void sleep(){53 System.out.println("Person sleep 8 hours every dat");54 }55

56 public void speak(){57 System.out.println("Hellow world, I am a person");58 }59 }60

61 =================第四部分:Tester03.java62 package com.oo.demo;63

64 public classTester03 {65 public static void main(String[] args) {66 System.out.println("===This is a person===");67 IAnimal person =new Person2();68 person.eat();69 person.run();70 person.sleep();71 person.speak();72

73 System.out.println("\n===This is a pig===");74 IAnimal pig =new Pig();75 pig.eat();76 pig.run();77 pig.sleep();78 pig.speak();79 }80 }81

82 java中的interface

View Code

2,为何要用接口

接口提取了一群类共同的函数,可以把接口当做一个函数的集合。

然后让子类去实现接口中的函数。

这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处在于:

1,归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

2,归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合

①就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。

②再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样

3,模仿interface

在python中根本就没有一个叫做interface的关键字,如果非要去模仿接口的概念

也可以使用继承,其实继承有两种用途

一:继承基类的方法,并且做出自己的改变或者扩展(代码重用):实践中,继承的这种用途意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

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

1 class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。

2 def read(self): #定接口函数read

3 pass

4

5 def write(self): #定义接口函数write

6 pass

7

8

9 class Txt(Interface): #文本,具体实现read和write

10 defread(self):11 print('文本数据的读取方法')12

13 defwrite(self):14 print('文本数据的读取方法')15

16 class Sata(Interface): #磁盘,具体实现read和write

17 defread(self):18 print('硬盘数据的读取方法')19

20 defwrite(self):21 print('硬盘数据的读取方法')22

23 classProcess(Interface):24 defread(self):25 print('进程数据的读取方法')26

27 defwrite(self):28 print('进程数据的读取方法')

View Code

4,抽象类

1,什么是抽象类

与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

2,为什么要有抽象类

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。

比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。

从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。

从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案

3,在python中实现抽象类

1 #一切皆文件

2 import abc #利用abc模块实现抽象类

3

4 class All_file(metaclass=abc.ABCMeta):5 all_type='file'

6 @abc.abstractmethod #定义抽象方法,无需实现功能

7 defread(self):8 '子类必须定义读功能'

9 pass

10

11 @abc.abstractmethod #定义抽象方法,无需实现功能

12 defwrite(self):13 '子类必须定义写功能'

14 pass

15

16 #class Txt(All_file):

17 #pass

18 #19 #t1=Txt() #报错,子类没有定义抽象方法

20

21 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法

22 defread(self):23 print('文本数据的读取方法')24

25 defwrite(self):26 print('文本数据的读取方法')27

28 class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法

29 defread(self):30 print('硬盘数据的读取方法')31

32 defwrite(self):33 print('硬盘数据的读取方法')34

35 class Process(All_file): #子类继承抽象类,但是必须定义read和write方法

36 defread(self):37 print('进程数据的读取方法')38

39 defwrite(self):40 print('进程数据的读取方法')41

42 wenbenwenjian=Txt()43

44 yingpanwenjian=Sata()45

46 jinchengwenjian=Process()47

48 #这样大家都是被归一化了,也就是一切皆文件的思想

49 wenbenwenjian.read()50 yingpanwenjian.write()51 jinchengwenjian.read()52

53 print(wenbenwenjian.all_type)54 print(yingpanwenjian.all_type)55 print(jinchengwenjian.all_type)

再来通过另外一个例子看看其中的差别和演变

先来看看原代码

1 classPeople:2 defwalk(self):3 print('is walking')4

5 classPig:6 defrun(self):7 print('is running')8

9 classDog:10 defjump(self):11 print('is jumping')12

13 people1 =People()14 pig1 =Pig()15 dog1 =Dog()16

17 #下面每个对象都有相应的走的功能,但是需要调用不用的方法。需要找个类把所有的方法统一起来。

18 people1.walk()19 pig1.run()20 dog1.jump()

View Code

但是,这样会造成的困扰就是会增加后面使用者的使用难度,同样是走的动作,但是却有不同的表达

这时,我们可以使用抽象类,达到归一化

1 #通过调用模块,装饰器等,可以实现后面的调用一定要调用Animal里面的方法,否则会报错

2 importabc3 class Animal(metaclass=abc.ABCMeta): #抽象类只能被继承,不能被实例化,功能是指规范子类

4 all_type = 'animal'

5 @abc.abstractmethod6 defrun(self):7 pass

8 @abc.abstractmethod9 defeat(self):10 pass

11

12 #抽象类不能被实例化,否则会报错

13 #animal = Animal() # Can't instantiate abstract class Animal with abstract methods eat, run

14

15 classPeople(Animal):16 defrun(self):17 print('people is running')18 defeat(self):19 print('people is eating')20

21 classPig(Animal):22 defrun(self):23 print('pig is running')24 defeat(self):25 print('pig is eating')26

27 classDog(Animal):28

29 defrun(self):30 print('dog is running')31 defeat(self):32 print('dog is eating')33

34 people1 =People()35 pig1 =Pig()36 dog1 =Dog()37

38 people1.run()39 pig1.run()40 dog1.run()41 print(people1.all_type) #虽然Animal是抽象类,但本质上还是一个类。

View Code

4. 抽象类与接口

抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。

抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

8,多态

多态指的是一类事物有多种形态,比如动物有多种形态:人,狗,猪

1 importabc2 class Animal(metaclass=abc.ABCMeta): #同一类事物:动物

3 @abc.abstractmethod4 deftalk(self):5 pass

6

7 class People(Animal): #动物的形态之一:人

8 deftalk(self):9 print('say hello')10

11 class Dog(Animal): #动物的形态之二:狗

12 deftalk(self):13 print('say wangwang')14

15 class Pig(Animal): #动物的形态之三:猪

16 deftalk(self):17 print('say aoao')

1,多态性

(1)什么是多态动态绑定(在继承的背景下使用,又是也称为多态性)

多态性是指在不考虑实例类型的情况下使用实例,多态性分为静态多态性和动态多态性

静态多态性:如任何类型都可以用运算符 + 进行运算,1+2=3。

动态多态性:如下

1 peo=People()2 dog=Dog()3 pig=Pig()4

5 #peo、dog、pig都是动物,只要是动物肯定有talk方法

6 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用

7 peo.talk()8 dog.talk()9 pig.talk()10

11 #更进一步,我们可以定义一个统一的接口来使用

12 deffunc(obj):13 obj.talk()

(2)为什么要用多态性(多态性的好处)

其实大家从上面多态性的例子可以看出,我们并没有增加什么新的知识,也就是说python本身就是支持多态性的,这么做的好处是什么呢?

1.增加了程序的灵活性

以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)

2.增加了程序额可扩展性

通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

1 >>> class Cat(Animal): #属于动物的另外一种形态:猫

2 ... deftalk(self):3 ... print('say miao')4 ...5 >>> def func(animal): #对于使用者来说,自己的代码根本无需改动

6 ... animal.talk()7 ...8 >>> cat1=Cat() #实例出一只猫

9 >>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能

10 say miao11

12 '''

13 这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)

2,鸭子类型(理论理解)

逗比时刻:

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

1 #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用

2 classTxtFile:3 defread(self):4 pass

5

6 defwrite(self):7 pass

8

9 classDiskFile:10 defread(self):11 pass

12 defwrite(self):13 pass

例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系

1 #str,list,tuple都是序列类型

2 s=str('hello')3 l=list([1,2,3])4 t=tuple((4,5,6))5

6 #我们可以在不考虑三者类型的前提下使用s,l,t

7 s.__len__()8 l.__len__()9 t.__len__()10

11 len(s)12 len(l)13 len(t)

9,封装

引子

封装有点像拿一个麻袋,把一个东西给装起来,然后封上口子,照这种逻辑来看,封装=‘隐藏’,这种理解还是有点片面

1,先看如何隐藏

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

1 #其实这仅仅这是一种变形操作

2 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

3

4 classA:5

6 __x = 1 #_A__x = 1 # 类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N

7

8 def __init__(self, name):9 self.__X = 10 #变形为self._A__X

10 self.name = name #self._A__name = name

11

12 def __foo(self): #def _A__foo(self):

13 print('from _foo')14

15 defbar(self):16 self.__foo() #self._A__foo() # 只有在类内部才可以通过__foo的形式访问到,在类定义的时候,就已经形成了这种格式了

17 print('from bar')18

19 a = A('egon')20

21 print(a.__dict__)22 print(A.__dict__)23 print(a.bar())24 #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

25

26 """

27 执行结果28 {'_A__X': 10, 'name': 'egon'}29 {'__module__': '__main__', '_A__x': 1, '__init__': ,
'_A__foo': , 'bar': ,
'__dict__': ,
'__weakref__': , '__doc__': None}30 from _foo31 from bar32 None33 """

1,这种自动变形的特点:

1,在类外部无法直接 obj.__AttrName  2,在类内部是可以直接使用: obj.__AttrName  3,子类无法覆盖父类__开头的属性

1 #子类无法覆盖父类__开头的属性。

2 classFoo:3 def __func(self): #_Foo__func

4 print('from foo')5

6 classBar(Foo):7 def __func(self): #_Bar__func # 子类定义的和父类定义的表面上看起来一样,但是却实质上不一样,根本就不是一个名字

8 print('from bar')

2,这种变形需要注意的问题:

1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

2、变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

1 classB:2 def __init__(self, name):3 self.__name =name4 b = B('egon')5 print(b.__dict__)6 b.__age = 18

7 print(b.__dict__)
print(b.__age)8

9 ###

10 {'_B__name': 'xiong'}11 {'_B__name': 'xiong', '__age': 18}
18

3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

1 #正常情况

2 classA:3 deffoo(self):4 print('A.foo')5 defbar(self):6 print('A.bar')7 self.foo() #b.foo(),访问顺序,先访问自己,然后类,再到父类。self.foo(),父类中存在,先访问父类里面的

8

9 classB(A):10 deffoo(self):11 print('B.foo')12

13 b =B()14 b.bar()15

16 ###

17 A.bar18 B.foo19

20 #定义私有,通过这种方法,调用只能访问类本身里面的变量

21 classA:22 def __foo(self): #_A__foo,看上去和子类的__foo长得一样,实则在定义的时候,名称就已经发生了改变了

23 print('A.foo')24 defbar(self):25 print('A.bar')26 self.__foo() #self._A__foo()

27

28 classB(A):29 def __foo(self): #_B__foo

30 print('B.foo')31

32 b =B()33 b.bar()34

35 ###

36 A.bar37 A.foo

2,封装的意义

1,封装数据属性:

1 #封装数据属性:明确的区分内外,控制外部对隐藏属性的操作。

2 classPeople:3 def __init__(self, name, age):4 self.__name =name5 self.__age =age6 deftell_info(self):7 print('Name:%s Age:%s' % (self.__name, self.__age))8 defset_info(self, name, age):9 if not isinstance(name, str): #isinstance 什么必须是什么的实例,这里name必须是str

10 print('名字必须是字符串类型')11 return

12 if notisinstance(age, int):13 print('年龄必须是整数类型')14 return

15 self.__name =name16 self.__age =age17

18

19 p = People('xiong', 18)20 print(p._People__name) #硬要访问

21 p.tell_info()22 p.set_info('xiong', 17)23 p.tell_info()24

25 ###

26 xiong27 Name:xiong Age:18

28 Name:xiong Age:17

2,封装方法:隔离复杂度

1 #封装方法:隔离复杂度

2 #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱

3 #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做

4 #隔离了复杂度,同时也提升了安全性

5

6 classATM:7 def __card(self):8 print('插卡')9 def __auth(self):10 print('用户认证')11 def __input(self):12 print('输入取款金额')13 def __print_bill(self):14 print('打印账单')15 def __take_money(self):16 print('取款')17

18 defwithdraw(self):19 self.__card()20 self.__auth()21 self.__input()22 self.__print_bill()23 self.__take_money()24

25 a =ATM()26 a.withdraw()

封装方法的其他举例:

你的身体没有一处不体现着封装的概念:你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,),你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。

电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!

快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

3,封装与可拓展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑

1 classRoom:2 def __init__(self, name, owner, weight, length):3 self.name =name4 self.owner =owner5

6 self.__length =length7 self.__weight =weight8 deftell_area(self):9 return self.__weight * self.__length

10 defroom_type(self):11

12 print("%s lives in the %s, the squre is %s" %(self.owner, self.name, self.tell_area()))13

14 r = Room('豪华公寓', 'xiong', 50, 50)15 print(r.tell_area())16 print(r.room_type())

4,property的使用

什么是特性property

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

例一:

BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:

过轻:低于18.5

正常:18.5-23.9

过重:24-27

肥胖:28-32

非常肥胖, 高于32

体质指数(BMI)=体重(kg)÷身高^2(m)

EX:70kg÷(1.75×1.75)=22.86

1 #方式一:

2 classPeople:3 def __init__(self, name, weight, height):4 self.name =name5 self.weight =weight6 self.height =height7

8 p = People('xiong',70,1.75)9 p.bmi = p.weight/(p.height ** 2)10 print(p.bmi)11

12 #方式二:通过函数,有括号,容易给使用者造成误解

13 classPeople:14 def __init__(self, name, weight, height):15 self.name =name16 self.weight =weight17 self.height =height18 defbmi(self):19 return self.weight / (self.height**2)20

21 p1 = People('xiong',70,1.75)22 print(p1.bmi())

1 #方式三:使用property装饰器特性实现功能

2 #实现统一访问,将方法伪装起来,bmi不能赋值(报错),且只能return返回。

3 classPeople:4 def __init__(self, name, weight, height):5 self.name =name6 self.weight =weight7 self.height =height8 @property9 defbmi(self):10 return self.weight / (self.height**2)11

12 p1 = People('xiong',75,1.8)13 print(p1.bmi)14 #p1.bmi = 1 # AttributeError: can't set attribute

例二:

将一个要通过计算的属性,封装成一个用户访问就像访问一个数据属性就可以的类型,伪装成一个更简单的方法,方便用户使用

classPeople:def __init__(self, name):

self.__name =namedefget_name(self):return self.__namep= People('xiong')print(p.get_name())###

xiong

未进行改变的

通过property进行封装:

1 classPeople:2 def __init__(self, name):3 self.__name =name4 @property #访问,查看,将一个要通过计算的属性,封装成一个用户访问就像访问一个数据属性就可以了,伪装

5 defname(self):6 return self.__name

7 @name.setter #修改

8 defname(self, val):9 if notisinstance(val, str):10 print('名字必须是字符串类型')11 return

12 self.__name =val13

14 @name.deleter #删除

15 defname(self):16 print("deleter")17 print('不允许删除')18 p = People('xiong')19 print(p.name) #访问行为

20

21 p.name = 'xiong' #修改行为

22 print(p.name)23

24 delp.name25

26 ###

27 xiong28 xiong29 deleter30 不允许删除

1 classPeople:2 def __init__(self, name):3 self.__name =name4 @property #访问,查看,将一个要通过计算的属性,封装成一个用户访问就像访问一个数据属性就可以了,伪装

5 defname(self):6 return self.__name

7 @name.setter #修改

8 defname(self, val):9 if notisinstance(val, str):10 print('名字必须是字符串类型')11 return

12 self.__name =val13

14 @name.deleter #删除

15 defname(self):16 print("deleter")17 print('不允许删除')18 p = People('xiong')19 print(p.name) #访问行为

20

21 p.name = 'XIONG' #修改行为

22 print(p.name)23

24 delp.name25

26 ###

27 xiong28 XIONG29 deleter30 不允许删除

为什么要用property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则,不需要添加多余的括号什么的,直接访问就可以了。

10,绑定方法与非绑定方法

1,类中定义的两大类函数

1 classFoo:2 def __init__(self, name):3 self.name =name4 def tell(self): #绑定对象的方法

5 print('名字是 %s'%self.name)6

7 @classmethod #绑定到类的方法,绑定给谁,就应该由谁来调用,谁调用,就把谁当第一个参数传入

8 def func(cls): #cls = Foo

9 print(cls)10

11 @staticmethod #非绑定方法,就是一个普通的函数,谁都能用,无自动传进

12 deffunc1(x, y):13 print(x +y)14

15 f = Foo('xiong')16 print(Foo.tell)17 print(f.tell)18

19 f.tell()20

21 Foo.func()22 print(Foo)23

24 print(Foo.func1) #这里证明Foo.func1和f.func1都是普通函数

25 print(f.func1)26 print(Foo.func1(3, 4))27 print(f.func1(3, 4))28

29 ###

30

31 >

32 名字是 xiong33

34

35

36

37 7

38 None39 7

40 None

一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

1,绑定到类的方法:用classmethod装饰器装饰的方法。

为类量身定制

类.boud_method(),自动将类当作第一个参数传入

(其实对象也可调用,但仍将类当作第一个参数传入)

2,绑定到对象的方法:没有被任何装饰器装饰的方法。

为对象量身定制

对象.boud_method(),自动将对象当作第一个参数传入

(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

二:非绑定方法:用 staticmethod 装饰器装饰的方法,实际上就是一个普通函数

不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已,对象和类都可以使用

注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

2,绑定方法与非绑定方法的使用

1,绑定方法的使用

1 importhashlib2 importtime3 importsettings4

5 classPeople:6 def __init__(self, name, age, sex):7

8 self.name =name9 self.age =age10 self.sex =sex11

12 def tell_info(self): #绑定到对象的方法

13 print('Name:%s Age:%s Sex:%s' %(self.name, self.age, self.sex))14

15 @classmethod #绑定到类的方法

16 deffrom_conf(cls):17 obj =cls(18 settings.name,19 settings.age,20 settings.sex21 )22 returnobj23

24 p = People('xiong', 18, 'male')25

26 #绑定给对象,就应该由对象来调用,自动将对象本身当做第一个参数传入

27 #People.tell_info(p)

28 p.tell_info()29

30 #绑定到类,就应该有类来调用,自动将类本身当做第一个参数传入

31 p = People.from_conf() #from_conf(People) # 从配置文件里面读取配置进行实例化

32 #等价于 p = People('xiongwu', 19, 'female')

33

34 p.tell_info()35

36

37 #name = 'xionghao'

38 #age = 19

39 #sex = 'female'

40

41 ###

42 Name:xiong Age:18Sex:male43 Name:xionghao Age:19 Sex:female

2,非绑定方法的使用

1 importhashlib2 importtime3 importsettings4

5 classPeople:6 def __init__(self, name, age, gender):7 self.name =name8 self.age =age9 self.gender =gender10

11 self.id =self.create_id()12

13 def tell_info(self): #绑定到对象的方法

14 print('Name:%s Age:%s Gender:%s' %(self.name, self.age, self.gender))15

16 @classmethod17 deffrom_conf(cls):18 obj =cls(19 settings.name,20 settings.age,21 settings.gender22 )23 returnobj24

25 @staticmethod26 defcreate_id():27 m =hashlib.md5()28 m.update(str(time.time()).encode('utf-8'))29 returnm.hexdigest()30

31

32 p = People('xiong', 18, '男')33 p1 = People('xiongh', 19, '男')34 p2 = People('xionghao', 20, '男')35

36 #非绑定方法,不与类或对象绑定,谁都可以调用,没有自动传值一说

37 print(p.id,'111')38 print(p1.id,'2221')39 print(p2.id,'331')40

41 ###

42 7871d235ee8ea3e25f16178004e00eb3 111

43 7871d235ee8ea3e25f16178004e00eb3 2221

44 7871d235ee8ea3e25f16178004e00eb3 331

45 7871d235ee8ea3e25f16178004e00eb3 111

46 4e16523b5ec7f189d45d1b9cda5da01e 2221

47 4e16523b5ec7f189d45d1b9cda5da01e 331

11,反射

1,引出反射

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

以下代码可以说明没有反射不具备的功能。

1 classPeople:2 def __init__(self, name, age):3 self.name =name4 self.age =age5

6 deftalk(self):7 print('%s is talking' %self.name)8

9 obj = People('xiong', 18)10

11 print(obj.name) #本质obj.__dict__['name'] # 访问数据属性,点后面是一个属性,而不是字符串

12 print(obj.talk) #访问绑定方法

13

14 ###

15 xiong16 >

注意:

不能直接调用input里面的内容,它仅仅只是一个字符串choice = input('>>>: ') # choice = 'name' # 这个name仅仅只是一个字符串print(obj.choice) # print(obj.'name') # 后面要是一个属性名,不能为字符串 需要解决的问题: 能够让用户通过字符串去映射到对象的一个属性身上,所以我们学习python的解决方式

2,通过字符串映射到对象

四个可以实现自省的函数 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

1,hasattr(object,name)

第一个参数为对象,第二个参数是字符串hasattr(o, name:str)

判断object中有没有一个name字符串对应的方法或属性

2,getattr(object, name, default=None)

得到对象的属性

3,setattr(x, y, v)

修改对象的属性

4,delattr(x, y)

删除对象的属性

1 classPeople:2 def __init__(self, name, age):3 self.name =name4 self.age =age5

6 deftalk(self):7 print('%s is talking' %self.name)8

9 obj = People('xiong', 18)10

11 print(obj.name) #本质obj.__dict__['name'] # 访问数据属性,点后面是一个属性,而不是字符串

12 print(obj.talk) #访问绑定方法

13

14

15 #查

16 print(hasattr(obj, 'name')) #obj.name # 实质 obj.__dict__ ['name']

17 print(hasattr(obj, 'talk'))18

19 #取得

20 print(getattr(obj, 'name', None)) #得到对象的属性

21 print(getattr(obj, 'talk', None)) #得到方法的属性

22

23 #修改

24 setattr(obj, 'sex', 'male') #本质:obj.sex = 'male'

25 print(obj.sex)26

27 #删除

28 delattr(obj, 'age') #del obj.age

29 print(obj.__dict__)30

31 ###

32 xiong33 >

34 True35 True36 xiong37 >

38 male39 {'name': 'xiong', 'sex': 'male'}

3,反射的应用

1 #反射的应用

2 classService:3 defrun(self):4 whileTrue:5 inp = input('>>>:').strip() #get a.txt

6 cmds = inp.split() #cmds = ['get', 'a.txt']

7

8 #print(cmds)

9 ifhasattr(self, cmds[0]):10 func =getattr(self, cmds[0])11 func(cmds)12

13 defget(self, cmds):14 print('get..............', cmds)15

16 defput(self, cmds):17 print('put..............', cmds)18

19

20 obj =Service()21 obj.run()22

23 ###输入get a.txt或者put a.txt

24 >>>: get a.txt25 get.............. ['get', 'a.txt']26 >>>: put a.txt27 put.............. ['put', 'a.txt']

12,内置方法介绍

1,isinstance(obj,cls)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象或实例

1 #isinstance

2 classFoo(object):3 pass

4 obj =Foo()5 print(isinstance(obj, Foo))6

7 ###

8 True

2,issubclass(sub,super)

检查sub类是否是 super 类的派生类(子类是不是父类的儿子)

1 #issubclass

2 classFoo(object):3 pass

4 classBar(Foo):5 pass

6 print(issubclass(Bar, Foo))7

8 ###

9 True

3,item系列

item系列,把对象做成像字典一样的东西,然后像字典一样去操作

1 #item系列,把对象做成像字典一样的东西,然后像字典一样去操作

2 class Foo: #将Foo模拟成Dict系列

3 def __init__(self, name):4 self.name =name5 def __getitem__(self, item): #item = 'name'

6 return self.__dict__.get(item) #这里使用self.__dict__[item],如果找不到这个key,就会报错。

7

8 def __setitem__(self, key, value):9 print('setitem...')10 print(key, value)11 self.__dict__[key] =value12

13 def __delitem__(self, key):14 print('delitem...')15 print(key)16 del self.__dict__[key]17

18 obj = Foo('xiong')19

20 #查看属性

21 #obj.属性名 # 一般或许对象属性的方法

22 print(obj['name']) #完成obj.name的取值效果

23

24 #设置属性

25 #obj.sex = 'male'

26 obj['sex'] = 'male'

27 #print(obj.__dict__)

28 #print(obj.sex)

29

30 #删除属性

31 #del obj.name

32 del obj['name']33 print(obj.__dict__)34

35 ###

36 xiong37 setitem...38 sex male39 delitem...40 name41 {'sex': 'male'}

4,__str__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

__str__方法定义完以后,会在打印对象的时候触发对象下面的__str__方法,将返回的结果(字符串)作为打印的结果,达到返回的不是说明obj是一个对象,而是得到更多有用的信息。

1 d = dict({'name': 'xiong'}) #本质是调dict这个类,然后将参数传进来进行实例化

2 print(isinstance(d, dict)) #dict就是d的一个对象

3 #数据类型就是类

4 print(d)5

6 classPeople():7 def __init__(self, name, age):8 self.name =name9 self.age =age10 obj =People11 print(obj)12 #上面两个print,python自带的类专门定制了打印的效果,打印出来的更加有用

13 #为People类定制一个方法,在print对象的时候自动触发对象的一个绑定方法,来返回有用的信息。

14 classPeople():15 def __init__(self, name, age):16 self.name =name17 self.age =age18

19 def __str__(self):20 print('===>str')21 return '' % (self.name, self.age) #打印obj直接触发__str__,并且打印返回值

22

23 obj = People('xiong', 18)24 print(obj) #触发并打印 obj.__str__()

25

26 ###

27 True28 {'name': 'xiong'}29

30 ===>str31

5,__del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__

来比较下下面两个程序的运行顺序

1 #程序1

2 classFoo:3

4 def __del__(self):5 print('执行我啦')6

7 f1=Foo()8 delf19 print('------->')10

11 #输出结果

12 执行我啦13 ------->

14

15 #程序2

16 classFoo:17

18 def __del__(self):19 print('执行我啦')20

21 f1=Foo()22 #del f1

23 print('------->')24

25 #输出结果

26 ------->

27 执行我啦28

29

30 #对比这两个程序的执行顺序,可以发现如果未执行程序del f1,则对象中的__del__是在最后执行的,这就是因为若没有手动执行del f1,方法__del__会自动帮你在程序结束的时候自动回收空间,而del f1则是自己手动执行空间的回收

典型的应用场景:

创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

这与文件处理是一个道理:

1 #__del__

2 f = open('settings.py') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件

3 del f #只回收用户空间的f,操作系统的文件还处于打开状态

4

5 #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是

6 f = open('settings.py')7 #读写...

8 f.close() #回收操作系统的资源

9 #很多情况下大家都容易忽略f.close,这就用到了with上下文管理

10

11

12 #模拟打开文件操作过程

13 classOpen:14 def __init__(self, filename):15 print('open file...')16 self.filename =filename17

18 def __del__(self): #在对象被删除的时候会先自动触发这个方法的执行,再把对象删掉

19 #这里还可以写跟资源回收相关的操作

20 print('回收操作系统的资源,类似于self.close()操作')21

22 f = Open('settings.py')23 #del f # 手动回收

24 print('-------end--------') #触发了 del f # f.__del__()

13,元类

1,元类介绍

1,exec用法

1 #储备知识 exec

2 #参数1:字符串形式的命令

3 #参数2:全局作用域(字典形式),如果不指定默认就使用globals()

4 #参数3:局部作用域(字典形式),如果不指定默认就使用locals()

5 #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中

6

7 g ={8 'x': 1,9 'y': 2

10 }11 l ={}12 exec("""

13 global x,m14 x = 1015 m = 10016

17 z = 318 """, g, l)19 print(g)20 print(l)

2,何为元类

python中一切皆对象,对象可以怎么用?1,都可以被引用,赋值给一个变量,x = obj2,都可以当作函数的参数传入3,都可以当作函数的返回值4,都可以当作容器类的元素,l = [func, time, obj, l]# 类也是对象

1 classFoo:2 pass

3 obj =Foo()4 print(type(obj))5 print(type(Foo))
###

所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象obj是调用类Foo得到的,如果一切皆为对象,那么类Foo本质也是一个对象,既然所有的对象都是调用类得到的,那么Foo必然也是调用了一个类得到的,这个类称为元类。  产生类的类称之为元类,默认所有用class定义的类,他们的元类就是type。元类type ===实例化===> 类Foo ===实例化===> 对象obj

2,创建类的两种方式

方式一:使用class关键字

1 #方式一:class关键字

2 class Chinese: #Cinese = type(...)

3 country = 'China'

4 def __init__(self,name, age):5 self.name =name6 self.age =age7

8 deftalk(self):9 print('%s is talking' %self.name)10 #print(Chinese)

11 obj = Chinese('xiong',18)12 print(obj, obj.name, obj.age)13

14 ###

15 <__main__.chinese object at> xiong 18

方式二:type元类产生

就是手动模拟class创建类的过程:将创建类的步骤拆分开,手动去创建

定义类的三要素类名 class_name = 'Chinese'基类们class_bases = (object, )类的名称空间class_dic,类的名称空间是执行类体代码而得到的调用type时会依次传入以上三个参数

1 #准备工作:

2

3 #创建类主要分为三部分

4   1类名5   2类的父类6   3类体7

8 #类名

9 class_name='Chinese'

10

11 #类的父类

12 class_bases=(object,)13

14 #类体

15 class_body="""

16 country='China'17 def __init__(self,name,age):18 self.name=name19 self.age=age20 def talk(self):21 print('%s is talking' %self.name)22 """

步骤一(先处理类体->名称空间):

类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典

class_dic={}

exec(class_body,globals(),class_dic)

print(class_dic)

#{'country': 'China', 'talk': , '__init__': }

步骤二:调用元类type(也可以自定义)来产生类Chinese1

1 hinese1=type(class_name,class_bases,class_dic) #实例化type得到对象Chinese1,即我们用class定义的类Chinese12 obj1 = Chinese1('xiong', 18)
print(obj1, obj1.name, obj1.age)3 print(Chinese1)4 print(type(Chinese1))5 print(isinstance(Chinese1,type))6 '''<__main__.chinese object at> xiong 18

7

8

9 True10 '''

我们看到,type 接收三个参数:

第 1 个参数是字符串 ‘Chinese1’,表示类名

第 2 个参数是元组 (object, ),表示所有的父类

第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法

补充:若Chinese1类有继承,即class Chinese1(Bar):.... 则等同于type('Chinese1',(Bar,),{})

3,自定义元类控制类的行为

一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

1 #!/usr/bin/env python2 # -*- coding: utf-8 -*-

3

4 # 知识储备:5 # 产生的新对象 = object.__new__(继承object类的子类)6

7 # 步骤一:如果说People=type(类名,类的父类们,类的名称空间),那么我们定义元类如下,来控制类的创建8

9 classMymeta(type): # 继承默认元类的一堆属性10 def __init__(self, class_name, class_bases, class_dic):11 if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():12 raise TypeError('必须为类指定文档注释')13

14 ifnot class_name.istitle():15 raise TypeError('类名首字母必须大写')16

17 super(Mymeta, self).__init__(class_name, class_bases, class_dic)18

19

20 class People(object, metaclass=Mymeta):21 country = 'China'

22

23 def __init__(self, name, age):24 self.name =name25 self.age =age26

27 def talk(self):28 print('%s is talking' %self.name)29

30 # 步骤二:如果我们想控制类实例化的行为,那么需要先储备知识__call__方法的使用31

32 class People(object,metaclass=type):33 def __init__(self,name,age):34 self.name=name35 self.age=age36

37 def __call__(self, *args, **kwargs):38 print(self,args,kwargs)39

40

41 # 调用类People,并不会出发__call__42 obj = People('egon', 18)43

44 # 调用对象obj(1,2,3,a=1,b=2,c=3),才会出发对象的绑定方法obj.__call__(1,2,3,a=1,b=2,c=3)45 obj(1, 2, 3, a=1, b=2, c=3) # 打印:<__main__.people object at> (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}46

47 # 总结:如果说类People是元类type的实例,那么在元类type内肯定也有一个__call__,会在调用People('egon',18)时触发执行,然后返回一个初始化好了的对象obj48

49 # 步骤三:自定义元类,控制类的调用(即实例化)的过程50 classMymeta(type): # 继承默认元类的一堆属性51 def __init__(self, class_name, class_bases, class_dic):52 ifnot class_name.istitle():53 raise TypeError('类名首字母必须大写')54

55 super(Mymeta, self).__init__(class_name, class_bases, class_dic)56

57 def __call__(self, *args, **kwargs):58 # self=People59 print(self, args, kwargs) # ('egon', 18) {}60

61 # 1、实例化People,产生空对象obj62 obj=object.__new__(self)63

64

65 # 2、调用People下的函数__init__,初始化obj66 self.__init__(obj, *args, **kwargs)67

68

69 # 3、返回初始化好了的obj70 returnobj71

72 class People(object,metaclass=Mymeta):73 country='China'

74

75 def __init__(self,name,age):76 self.name=name77 self.age=age78

79 def talk(self):80 print('%s is talking' %self.name)81

82 obj=People('egon',18)83 print(obj.__dict__) # {'name': 'egon', 'age': 18}84

85 # 步骤四:86 classMymeta(type): # 继承默认元类的一堆属性87 def __init__(self, class_name, class_bases, class_dic):88 ifnot class_name.istitle():89 raise TypeError('类名首字母必须大写')90

91 super(Mymeta, self).__init__(class_name, class_bases, class_dic)92

93 def __call__(self, *args, **kwargs):94 # self=People95 print(self, args, kwargs) # ('egon', 18) {}96

97 # 1、调用self,即People下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj98 obj = self.__new__(self, *args, **kwargs)99

100 # 2、一定记得返回obj,因为实例化People(...)取得就是__call__的返回值101 returnobj102

103 class People(object,metaclass=Mymeta):104 country = 'China'

105

106 def __init__(self, name, age):107 self.name =name108 self.age =age109

110 def talk(self):111 print('%s is talking' %self.name)112

113 def __new__(cls, *args, **kwargs):114 obj=object.__new__(cls)115 cls.__init__(obj, *args, **kwargs)116 returnobj117

118 obj=People('egon',18)119 print(obj.__dict__) # {'name': 'egon', 'age': 18}120

121 # 步骤五:基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存122 classMysql:123 __instance =None124 def __init__(self,host='127.0.0.1',port='3306'):125 self.host=host126 self.port=port127

128 @classmethod129 def singleton(cls,*args,**kwargs):130 ifnot cls.__instance:131 cls.__instance=cls(*args,**kwargs)132 returncls.__instance133

134

135 obj1 =Mysql()136 obj2 =Mysql()137 print(obj1 isobj2) # False138

139 obj3 =Mysql.singleton()140 obj4 =Mysql.singleton()141 print(obj3 isobj4) # True142

143 # 应用:定制元类实现单例模式144 classMymeta(type):145 def __init__(self, name, bases, dic): # 定义类Mysql时就触发146 self.__instance =None147 super().__init__(name, bases, dic)148

149 def __call__(self, *args, **kwargs): # Mysql(...)时触发150

151 ifnot self.__instance:152 self.__instance = object.__new__(self) # 产生对象153 self.__init__(self.__instance, *args, **kwargs) # 初始化对象154 # 上述两步可以合成下面一步155 # self.__instance=super().__call__(*args,**kwargs)156

157 returnself.__instance158 class Mysql(metaclass=Mymeta):159 def __init__(self, host='127.0.0.1', port='3306'):160 self.host =host161 self.port =port162

163

164 obj1 =Mysql()165 obj2 =Mysql()166

167 print(obj1 is obj2)

View Code

14,面向对象的软件开发

15,异常处理

1,何为异常

异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下

错误分为两种:

1,语法错误:这种错误过不了python解释器的语法检测,在程序执行前就要改正过来

1 #语法错误示范一2 if

3 #语法错误示范二4 def test:5 pass6 #语法错误示范三7 classFoo8 pass9 #语法错误示范四10 print(haha)

2,逻辑错误:

1 #TypeError:int类型不可迭代2 for i in 3:3 pass4 #ValueError5 num=input(">>:") #输入hello6 int(num)7

8 #NameError9 aaa10

11 #IndexError12 l=['xiong','aa']13 l[3]14

15 #KeyError16 dic={'name':'huihui'}17 dic['age']18

19 #AttributeError20 classFoo:pass21 Foo.x22

23 #ZeroDivisionError:无法完成计算24 res1=1/0

25 res2=1+'str'

2,异常的种类

在python中不同的异常可以用不同的类型(python中统一了类与类型,类型即类)去标识,一个异常标识一种错误

常见异常

1 AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x2 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 ArithmeticError2 AssertionError3 AttributeError4 BaseException5 BufferError6 BytesWarning7 DeprecationWarning8 EnvironmentError9 EOFError10 Exception11 FloatingPointError12 FutureWarning13 GeneratorExit14 ImportError15 ImportWarning16 IndentationError17 IndexError18 IOError19 KeyboardInterrupt20 KeyError21 LookupError22 MemoryError23 NameError24 NotImplementedError25 OSError26 OverflowError27 PendingDeprecationWarning28 ReferenceError29 RuntimeError30 RuntimeWarning31 StandardError32 StopIteration33 SyntaxError34 SyntaxWarning35 SystemError36 SystemExit37 TabError38 TypeError39 UnboundLocalError40 UnicodeDecodeError41 UnicodeEncodeError42 UnicodeError43 UnicodeTranslateError44 UnicodeWarning45 UserWarning46 ValueError47 Warning48 ZeroDivisionError49

50 更多异常

更多异常

3,异常处理

为了保证程序的健壮性与容错性,即在遇到错误时程序不会崩溃,我们需要对异常进行处理,

1,如果错误发生的条件是可预知的,我们需要用if进行处理:在错误发生之前进行预防

1 AGE=10

2 whileTrue:3 age=input('>>:').strip()4 if age.isdigit(): #只有在age为字符串形式的整数时,下列代码才不会出错,该条件是可预知的

5 age=int(age)6 if age ==AGE:7 print('you got it')8 break

2,如果错误发生的条件是不可预知的,则需要用到try...except:在错误发生之后进行处理

1 #基本语法为

2 try:3 被检测的代码块4 except异常类型:5 try中一旦检测到异常,就执行这个位置的逻辑6 #举例

7 try:8 f=open('a.txt')9 g=(line.strip() for line inf)10 print(next(g))11 print(next(g))12 print(next(g))13 print(next(g))14 print(next(g))15 exceptStopIteration:16 f.close()

4,try...except...详细用法

1.异常类只能用来处理指定的异常情况,如果非指定异常则无法处理

1 s1 = 'hello'

2 try:3 int(s1)4 except IndexError as e: #未捕获到异常,程序直接报错

5 print e

2.多分支,被检测的代码块抛出的异常有多种可能性,并且我们需要针对每一种都定制专门的处理逻辑

1 s1 = 'hello'

2 try:3 int(s1)4 exceptIndexError as e:5 print(e)6 exceptKeyError as e:7 print(e)8 exceptValueError as e:9 print(e)

3.万能异常Exception,被检测的代码块抛出的异常有多种可能性,并且我们针对所有的异常类型都只用一种处理逻辑就可以了。

1 s1 = 'hello'

2 try:3 int(s1)4 exceptException as e:5 print(e)

4.也可以在多分支后来一个Exception

1 s1 = 'hello'

2 try:3 int(s1)4 exceptIndexError as e:5 print(e)6 exceptKeyError as e:7 print(e)8 exceptValueError as e:9 print(e)10 exceptException as e:11 print(e)

5.异常的其他结构:finally,回收机制使用

1 s1 = 'hello'

2 try:3 int(s1)4 exceptIndexError as e:5 print(e)6 exceptKeyError as e:7 print(e)8 exceptValueError as e:9 print(e)10 #except Exception as e:

11 #print(e)

12 else:13 print('try内代码块没有异常则执行我')14 finally:15 print('无论异常与否,都会执行该模块,通常是进行清理工作')

6.主动触发异常:raise  异常类型(值)

try:raise TypeError('类型错误')exceptException as e:print(e)#或者下面程序你不想让用户传入数字当作名字

classPeople:def __init__(self, name, age):if notisinstance(name, str):raise TypeError('名字必须传入str类型')if notisinstance(age, int):raise TypeError('年龄必须传入int类型')

self.name=name

self.age=age

p= People(2222,18)###

这时如果用户输入数字当做用户名,就会直接报错了

7.自定义异常

1 classMynException(BaseException):2 def __init__(self,msg):3 self.msg=msg4 def __str__(self):5 returnself.msg6

7 try:8 raise MyException('类型错误')9 exceptMyException as e:10 print(e)

8.断言:assert 条件

1 info ={}2 info['name'] = 'xiong'

3 info['age'] = 18

4 assert('name' in info) and ('age' in info) #判断上面是否name and age in info.否则报错

9.总结try..except

1:把错误处理和真正的工作分开来

2:代码更易组织,更清晰,复杂的工作任务更容易实现;

3:毫无疑问,更安全了,不至于由于一些小的疏忽而使程序意外崩溃了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值