python-面向对象进阶

      python-面向对象进阶

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

1,初识继承

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

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

1 # 父类/基类/超生类
2 # 子类/派生类(继承父类)
3 # _bases_则是查看所有继承的父类

  代码示例如下:

 1 class ParentClass1:
 2     pass
 3 class ParentClass2:
 4     pass
 5 class SubClass1(ParentClass1):
 6     pass
 7 class SubClass2(ParentClass1,ParentClass2):
 8     pass
 9  
10 print(SubClass1.__bases__)
11 print(SubClass2.__bases__)
12  
13 ###
14 (<class '__main__.ParentClass1'>,)
15 (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

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

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

 抽象分成两个层次:

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

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

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

                              

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

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

                                 

3, 继承与重用性

 继承与重用性

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

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

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

 1 class Hero:
 2     def __init__(self, nickname, life_value, aggressivity):
 3         self.nickname = nickname
 4         self.life_value = life_value
 5         self.aggressivity = aggressivity
 6     def attack(self, enemy):
 7         enemy.life_value -= self.aggressivity
 8  
 9 class Gailen(Hero):
10     pass
11  
12 class Riven(Hero):
13     pass
14  
15 gailen = Gailen('草丛伦', 120, 40)
16 print(gailen.nickname, gailen.life_value, gailen.aggressivity)  # 这里能实现,说明了它是继承了Hero的属性

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

  再说属性查找

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

 1 # 查找顺序:对象自己 → 对象自己的类 → 父类
 2 class Foo:
 3     def f1(self):
 4         print('Foo.f1')
 5  
 6     def f2(self):
 7         print('Foo.f2')
 8         self.f1()  # b.f1()
 9  
10 class Bar(Foo):
11     def f1(self):
12         print('Bar.f1')
13  
14 b = Bar()
15 b.f2()
16 ###Foo.f2
17 ###Bar.f1

4,派生

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

 1 class Hero:   
 2     def __init__(self, nickname, life_value, aggressivity):
 3         self.nickname = nickname
 4         self.life_value = life_value
 5         self.aggressivity = aggressivity
 6     def attack(self, enemy):
 7         enemy.life_value -= self.aggressivity
 8  
 9 class Riven(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 class Riven(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 flying
19 比基尼
20  
21 '''

5,继承的实现原理

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

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

 1 class A(object):
 2     def test(self):
 3         print('from A')
 4  
 5 class B(A):
 6     def test(self):
 7         print('from B')
 8  
 9 class C(A):
10     def test(self):
11         print('from C')
12  
13 class D(B):
14     def test(self):
15         print('from D')
16  
17 class E(C):
18     def test(self):
19         print('from E')
20  
21 class F(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 D
31 (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
32 """

2,区别新式类和经典类

 1 # 在python2中-->经典类:没有继承object的类,以及它的子类都称经典类
 2 class Foo:
 3     pass
 4 class Bar(Foo):
 5     pass
 6  
 7 # 在python2中-->新式类:继承object的类,以及它的子类都称之为新式类
 8 class Foo(object):
 9     pass
10 class Bar(Foo):
11     pass
12  
13 # 在python3中-->新式类:一个类没有继承object类,默认就继承了object
14 class Foo():  # --> class Foo(object): 
15     pass
16 print(Foo.__bases__)
17  
18 ###
19 (<class 'object'>,)

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

                                        

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

                                        

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

  代码示例

 1 class A(object):
 2     def test(self):
 3         print('from A')
 4  
 5 class B(A):
 6     def test(self):
 7         print('from B')
 8  
 9 class C(A):
10     def test(self):
11         print('from C')
12  
13 class D(B):
14     def test(self):
15         print('from D')
16  
17 class E(C):
18     def test(self):
19         print('from E')
20  
21 class F(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 1
37 (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
38 '''

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

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

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

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

 1 class Hero:
 2     def __init__(self, nickname, life_value, aggressivity):
 3         self.nickname = nickname
 4         self.life_value = life_value
 5         self.aggressivity = aggressivity
 6     def attack(self, enemy):
 7         enemy.life_value -= self.aggressivity
 8  
 9 class Gailen(Hero):
10     camp = 'Demacia'
11     def attack(self, enemy):
12         Hero.attack(self, enemy)  # 指名道姓
13         print('from Gailen Class')
14  
15 class Riven(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 80
26 from Gailen Class
27 50
28 """
 1 class Hero:
 2     def __init__(self, nickname, life_value, aggressivity):
 3         self.nickname = nickname
 4         self.life_value = life_value
 5         self.aggressivity = aggressivity
 6     def attack(self, enemy):
 7         enemy.life_value -= self.aggressivity
 8  
 9 class Gailen(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 = weapon
18     def attack(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 class Hero:
 3     def __init__(self, nickname, life_value, aggressivity):
 4         self.nickname = nickname
 5         self.life_value = life_value
 6         self.aggressivity = aggressivity
 7     def attack(self, enemy):
 8         enemy.life_value -= self.aggressivity
 9  
10 class Gailen(Hero):
11     camp = 'Demacia'
12     def attack(self, enemy):
13         super(Gailen, self).attack(enemy)  # 依赖继承,super(自己的类名,self)
14         print('from Gailen Class')
15  
16 class Riven(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 80
28 from Gailen Class
29 50
30 """

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

 1 class Hero:
 2     def __init__(self, nickname, life_value, aggressivity):
 3         self.nickname = nickname
 4         self.life_value = life_value
 5         self.aggressivity = aggressivity
 6     def attack(self, enemy):
 7         enemy.life_value -= self.aggressivity
 8  
 9 class Gailen(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 = weapon
18     def attack(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 class A:
 3     def f1(self):
 4         print('from A')
 5         super().f1()
 6 class B:
 7     def f1(self):
 8         print('from B')
 9 class C(A,B):
10     pass
11  
12 print(C.mro())
13 c = C()
14 c.f1()  # 打印结果:from B
15  
16 """
17 [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
18 from A
19 from B
20 """

6,组合(类的组合)

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

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

1,组合与重用性

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

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

  代码示例

 1 class People:
 2     def __init__(self, name, age, sex):
 3         self.name = name
 4         self.age = age
 5         self.sex = sex
 6  
 7 class Teacher(People):
 8     def __init__(self, name, age, sex, level, salary):
 9         super().__init__(name, age, sex)
10         self.level = level
11         self.salary = salary
12  
13 class Student(People):
14     def __init__(self, name, age, sex, class_time):
15         super().__init__(name, age, sex)
16         self.class_time = class_time
17  
18 class Course:
19     def __init__(self, course_name, course_price, course_period):
20         self.course_name = course_name
21         self.course_price = course_price
22         self.course_period = course_period
23     def tell_info(self):
24         print('课程名为<%s> 课程价钱为<%s> 课程周期为<%s>' % (self.course_name, self.course_price, self.course_period))
25  
26 class Date:
27     def __init__(self, year, mon, day):
28         self.year = year
29         self.mon = mon
30         self.day = day
31     def tell_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 = python
40 teacher2.course = python
41  
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 = python
54 student1.course2 = linux
55 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 = d
66 print(student1.birth.tell_info())
67 print(student1.course1.tell_info())
68  
69 """
70 <__main__.Course object at 0x02CECCF0>
71 <__main__.Course object at 0x02CECCF0>
72 <__main__.Course object at 0x02CECCF0>
73 python
74 课程名为<python> 课程价钱为<3000> 课程周期为<3mons>
75 课程名为<python> 课程价钱为<3000> 课程周期为<3mons>
76 课程名为<linux> 课程价钱为<2000> 课程周期为<4mons>
77 [<__main__.Course object at 0x02CECCF0>, <__main__.Course object at 0x02CECD10>]
78 I was born in 1999年4月20日
79 None
80 课程名为<python> 课程价钱为<3000> 课程周期为<3mons>
81 None
82 """

7,抽象类与归一化设计

1,什么是接口

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

  java中的interface:

 1 =================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java
 2 /*
 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.java
37 /*
38 *实现了IAnimal的“人”,有几点说明一下:
39 * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,<br>但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样
40 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,<br>这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */
41  
42 package com.oo.demo;
43 public class Person2 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.java
62 package com.oo.demo;
63  
64 public class Tester03 {
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的关键字,如果非要去模仿接口的概念

  可以借助第三方模块:http://pypi.python.org/pypi/zope.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     def read(self):
11         print('文本数据的读取方法')
12  
13     def write(self):
14         print('文本数据的读取方法')
15  
16 class Sata(Interface): #磁盘,具体实现read和write
17     def read(self):
18         print('硬盘数据的读取方法')
19  
20     def write(self):
21         print('硬盘数据的读取方法')
22  
23 class Process(Interface):
24     def read(self):
25         print('进程数据的读取方法')
26  
27     def write(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     def read(self):
 8         '子类必须定义读功能'
 9         pass
10  
11     @abc.abstractmethod #定义抽象方法,无需实现功能
12     def write(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     def read(self):
23         print('文本数据的读取方法')
24  
25     def write(self):
26         print('文本数据的读取方法')
27  
28 class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
29     def read(self):
30         print('硬盘数据的读取方法')
31  
32     def write(self):
33         print('硬盘数据的读取方法')
34  
35 class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
36     def read(self):
37         print('进程数据的读取方法')
38  
39     def write(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 class People:
 2     def walk(self):
 3         print('is walking')
 4  
 5 class Pig:
 6     def run(self):
 7         print('is running')
 8  
 9 class Dog:
10     def jump(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 import abc
 3 class Animal(metaclass=abc.ABCMeta):  # 抽象类只能被继承,不能被实例化,功能是指规范子类
 4     all_type = 'animal'
 5     @abc.abstractmethod
 6     def run(self):
 7         pass
 8     @abc.abstractmethod
 9     def eat(self):
10         pass
11  
12 # 抽象类不能被实例化,否则会报错
13 # animal = Animal()  # Can't instantiate abstract class Animal with abstract methods eat, run
14  
15 class People(Animal):
16     def run(self):
17         print('people is running')
18     def eat(self):
19         print('people is eating')
20  
21 class Pig(Animal):
22     def run(self):
23         print('pig is running')
24     def eat(self):
25         print('pig is eating')
26  
27 class Dog(Animal):
28  
29     def run(self):
30         print('dog is running')
31     def eat(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 import abc
 2 class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
 3     @abc.abstractmethod
 4     def talk(self):
 5         pass
 6  
 7 class People(Animal): #动物的形态之一:人
 8     def talk(self):
 9         print('say hello')
10  
11 class Dog(Animal): #动物的形态之二:狗
12     def talk(self):
13         print('say wangwang')
14  
15 class Pig(Animal): #动物的形态之三:猪
16     def talk(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 def func(obj):
13     obj.talk()

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

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

  1.增加了程序的灵活性

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

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

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

 1 >>> class Cat(Animal): #属于动物的另外一种形态:猫
 2 ...     def talk(self):
 3 ...         print('say miao')
 4 ...
 5 >>> def func(animal): #对于使用者来说,自己的代码根本无需改动
 6 ...     animal.talk()
 7 ...
 8 >>> cat1=Cat() #实例出一只猫
 9 >>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
10 say miao
11  
12 '''
13 这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)

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

  逗比时刻:

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

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

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

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

 1 #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
 2 class TxtFile:
 3     def read(self):
 4         pass
 5  
 6     def write(self):
 7         pass
 8  
 9 class DiskFile:
10     def read(self):
11         pass
12     def write(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 class A:
 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     def bar(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__': <function A.__init__ at 0x02C1A390>, <br>'_A__foo': <function A.__foo at 0x02C1A3D8>, 'bar': <function A.bar at 0x02C1A420>, <br>'__dict__': <attribute '__dict__' of 'A' objects>, <br>'__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
30 from _foo
31 from bar
32 None
33 """

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

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

1 # 子类无法覆盖父类__开头的属性。
2 class Foo:
3     def __func(self):  # _Foo__func
4         print('from foo')
5      
6 class Bar(Foo):
7     def __func(self):  # _Bar__func  # 子类定义的和父类定义的表面上看起来一样,但是却实质上不一样,根本就不是一个名字
8        print('from bar')

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

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

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

 1 class B:
 2     def __init__(self, name):
 3         self.__name = name
 4 b = B('egon')
 5 print(b.__dict__)
 6 b.__age = 18
 7 print(b.__dict__)<br>print(b.__age)
 8  
 9 ###
10 {'_B__name': 'qiuma'}
11 {'_B__name': 'qiuma', '__age': 18}<br>18

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

 1 # 正常情况
 2 class A:
 3     def foo(self):
 4         print('A.foo')
 5     def bar(self):
 6         print('A.bar')
 7         self.foo()  # b.foo(),访问顺序,先访问自己,然后类,再到父类。self.foo(),父类中存在,先访问父类里面的
 8  
 9 class B(A):
10     def foo(self):
11         print('B.foo')
12  
13 b = B()
14 b.bar()
15  
16 ###
17 A.bar
18 B.foo
19  
20 # 定义私有,通过这种方法,调用只能访问类本身里面的变量
21 class A:
22     def __foo(self):  # _A__foo,看上去和子类的__foo长得一样,实则在定义的时候,名称就已经发生了改变了
23         print('A.foo')
24     def bar(self):
25         print('A.bar')
26         self.__foo()  # self._A__foo()
27  
28 class B(A):
29     def __foo(self):  # _B__foo
30         print('B.foo')
31  
32 b = B()
33 b.bar()
34  
35 ###
36 A.bar
37 A.foo

2,封装的意义

1,封装数据属性:

 1 # 封装数据属性:明确的区分内外,控制外部对隐藏属性的操作。
 2 class People:
 3     def __init__(self, name, age):
 4         self.__name = name
 5         self.__age = age
 6     def tell_info(self):
 7         print('Name:%s Age:%s' % (self.__name, self.__age))
 8     def set_info(self, name, age):
 9         if not isinstance(name, str):  # isinstance 什么必须是什么的实例,这里name必须是str
10             print('名字必须是字符串类型')
11             return
12         if not isinstance(age, int):
13             print('年龄必须是整数类型')
14             return
15         self.__name = name
16         self.__age = age
17  
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 xiong
27 Name:xiong Age:18
28 Name:xiong Age:17

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

 1 # 封装方法:隔离复杂度
 2 # 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
 3 # 对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
 4 # 隔离了复杂度,同时也提升了安全性
 5  
 6 class ATM:
 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     def withdraw(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()

封装方法的其他举例:

  1. 你的身体没有一处不体现着封装的概念:你的身体把膀胱尿道等等这些尿的功能隐藏了起来,然后为你提供一个尿的接口就可以了(接口就是你的。。。,),你总不能把膀胱挂在身体外面,上厕所的时候就跟别人炫耀:hi,man,你瞅我的膀胱,看看我是怎么尿的。
  2. 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!
  3. 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了

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

3,封装与可拓展性

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

 1 class Room:
 2     def __init__(self, name, owner, weight, length):
 3         self.name = name
 4         self.owner = owner
 5  
 6         self.__length = length
 7         self.__weight = weight
 8     def tell_area(self):
 9         return self.__weight * self.__length
10     def room_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('豪华公寓', 'qiuma', 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 class People:
 3     def __init__(self, name, weight, height):
 4         self.name = name
 5         self.weight = weight
 6         self.height = height
 7 
 8 p = People('xiong',70,1.75)
 9 p.bmi = p.weight/(p.height ** 2)
10 print(p.bmi)
11 
12 # 方式二:通过函数,有括号,容易给使用者造成误解
13 class People:
14     def __init__(self, name, weight, height):
15         self.name = name
16         self.weight = weight
17         self.height = height
18     def bmi(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 class People:
 4     def __init__(self, name, weight, height):
 5         self.name = name
 6         self.weight = weight
 7         self.height = height
 8     @property
 9     def bmi(self):
10         return self.weight / (self.height**2)
11  
12 p1 = People('qiuma',75,1.8)
13 print(p1.bmi)
14 # p1.bmi = 1  # AttributeError: can't set attribute

例二:

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

class People:
    def __init__(self, name):
        self.__name = name
    def get_name(self):
        return self.__name

p = People('xiong')
print(p.get_name())

###
xiong
未进行改变的

通过property进行封装:

 1 class People:
 2     def __init__(self, name):
 3         self.__name = name
 4     @property  # 访问,查看,将一个要通过计算的属性,封装成一个用户访问就像访问一个数据属性就可以了,伪装
 5     def name(self):
 6         return self.__name
 7     @name.setter  # 修改
 8     def name(self, val):
 9         if not isinstance(val, str):
10             print('名字必须是字符串类型')
11             return
12         self.__name = val
13  
14     @name.deleter  # 删除
15     def name(self):
16         print("deleter")
17         print('不允许删除')
18 p = People('qiuma')
19 print(p.name)  # 访问行为
20  
21 p.name = 'Qiuma'  # 修改行为
22 print(p.name)
23  
24 del p.name
25  
26 ###
27 qiuma
28 Qiuma
29 deleter
30 不允许删除
 1 class People:
 2     def __init__(self, name):
 3         self.__name = name
 4     @property  # 访问,查看,将一个要通过计算的属性,封装成一个用户访问就像访问一个数据属性就可以了,伪装
 5     def name(self):
 6         return self.__name
 7     @name.setter  # 修改
 8     def name(self, val):
 9         if not isinstance(val, str):
10             print('名字必须是字符串类型')
11             return
12         self.__name = val
13  
14     @name.deleter  # 删除
15     def name(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 del p.name
25  
26 ###
27 xiong28 XIONG29 deleter
30 不允许删除

为什么要用property

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

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

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

 1 class Foo:
 2     def __init__(self, name):
 3         self.name = name
 4     def tell(self):  # 绑定对象的方法
 5         print('名字是 %s '% self.name)
 6  
 7     @classmethod  # 绑定到类的方法,绑定给谁,就应该由谁来调用,谁调用,就把谁当第一个参数传入
 8     def func(cls):  # cls = Foo
 9         print(cls)
10  
11     @staticmethod  # 非绑定方法,就是一个普通的函数,谁都能用,无自动传进
12     def func1(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 <function Foo.tell at 0x03679420>
31 <bound method Foo.tell of <__main__.Foo object at 0x036FC910>>
32 名字是 xiong
33 <class '__main__.Foo'>
34 <class '__main__.Foo'>
35 <function Foo.func1 at 0x036794B0>
36 <function Foo.func1 at 0x036794B0>
37 7
38 None
39 7
40 None

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

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

  为类量身定制
       类.boud_method(),自动将类当作第一个参数传入
     (其实对象也可调用,但仍将类当作第一个参数传入)

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

  为对象量身定制
      对象.boud_method(),自动将对象当作第一个参数传入
    (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

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

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

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

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

  1,绑定方法的使用

 1 import hashlib
 2 import time
 3 import settings
 4  
 5 class People:
 6     def __init__(self, name, age, sex):
 7  
 8         self.name = name
 9         self.age = age
10         self.sex = sex
11  
12     def tell_info(self):  # 绑定到对象的方法
13         print('Name:%s Age:%s Sex:%s' % (self.name, self.age, self.sex))
14  
15     @classmethod  # 绑定到类的方法
16     def from_conf(cls):
17         obj = cls(
18             settings.name,
19             settings.age,
20             settings.sex
21         )
22         return obj
23  
24 p = People('qiuma', 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('qiumawu', 19, 'female')
33  
34 p.tell_info()
35  
36  
37 # name = 'xionghao'
38 # age = 19
39 # sex = 'female'
40  
41 ###
42 Name:xiong Age:18 Sex:male
43 Name:xionghao Age:19 Sex:female

  2,非绑定方法的使用

 1 import hashlib
 2 import time
 3 import settings
 4   
 5 class People:
 6     def __init__(self, name, age, gender):
 7         self.name = name
 8         self.age = age
 9         self.gender = gender
10   
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     @classmethod
17     def from_conf(cls):
18         obj = cls(
19             settings.name,
20             settings.age,
21             settings.gender
22         )
23         return obj
24   
25     @staticmethod
26     def create_id():
27         m = hashlib.md5()
28         m.update(str(time.time()).encode('utf-8'))
29         return m.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 class People:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5  
 6     def talk(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 xiong
16 <bound method People.talk of <__main__.People object at 0x0117EE70>>

注意:

不能直接调用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 class People:
 2     def __init__(self, name, age):
 3         self.name = name
 4         self.age = age
 5  
 6     def talk(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 xiong
33 <bound method People.talk of <__main__.People object at 0x00F2DD30>>
34 True
35 True
36 qiuma
37 <bound method People.talk of <__main__.People object at 0x00F2DD30>>
38 male
39 {'name': 'xiong', 'sex': 'male'}

3,反射的应用

 1 # 反射的应用
 2 class Service:
 3     def run(self):
 4         while True:
 5             inp = input('>>>: ').strip()  # get a.txt
 6             cmds = inp.split()  # cmds = ['get', 'a.txt']
 7  
 8             # print(cmds)
 9             if hasattr(self, cmds[0]):
10                 func = getattr(self, cmds[0])
11                 func(cmds)
12  
13     def get(self, cmds):
14         print('get..............', cmds)
15  
16     def put(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.txt
25 get.............. ['get', 'a.txt']
26 >>>: put a.txt
27 put.............. ['put', 'a.txt']

12,内置方法介

1,isinstance(obj,cls)

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

1 # isinstance
2 class Foo(object):
3     pass
4 obj = Foo()
5 print(isinstance(obj, Foo))
6  
7 ###
8 True

2,issubclass(sub,super)

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

1 # issubclass
2 class Foo(object):
3     pass
4 class Bar(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 = name
 5     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] = value
12  
13     def __delitem__(self, key):
14         print('delitem...')
15         print(key)
16         del self.__dict__[key]
17  
18 obj = Foo('qiuma')
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 qiuma
37 setitem...
38 sex male
39 delitem...
40 name
41 {'sex': 'male'}

4,__str__

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

自定制格式化字符串__format__

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

 1 d = dict({'name': 'qiuma'})  # 本质是调dict这个类,然后将参数传进来进行实例化
 2 print(isinstance(d, dict))  # dict就是d的一个对象
 3 # 数据类型就是类
 4 print(d)
 5  
 6 class People():
 7     def __init__(self, name, age):
 8         self.name = name
 9         self.age = age
10 obj = People
11 print(obj)
12 # 上面两个print,python自带的类专门定制了打印的效果,打印出来的更加有用
13 # 为People类定制一个方法,在print对象的时候自动触发对象的一个绑定方法,来返回有用的信息。<br><br>
14 class People():
15     def __init__(self, name, age):
16         self.name = name
17         self.age = age
18  
19     def __str__(self):
20         print('===>str')
21         return '<name:%s,age:%s>' % (self.name, self.age)  # 打印obj直接触发__str__,并且打印返回值
22  
23 obj = People('qiuma', 18)
24 print(obj)  # 触发并打印 obj.__str__()
25  
26 ###
27 True
28 {'name': 'qiuma'}
29 <class '__main__.People'>
30 ===>str
31 <name:qiuma,age:18>

5,__del__

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

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

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

 1 # 程序1
 2 class Foo:
 3  
 4     def __del__(self):
 5         print('执行我啦')
 6  
 7 f1=Foo()
 8 del f1
 9 print('------->')
10  
11 #输出结果
12 执行我啦
13 ------->
14  
15 # 程序2
16 class Foo:
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 class Open:
14     def __init__(self, filename):
15         print('open file...')
16         self.filename = filename
17  
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,m
14 x = 10
15 m = 100
16  
17 z = 3
18 """, g, l)
19 print(g)
20 print(l)

2,何为元类

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

# 类也是对象

1 class Foo:
2 pass
3 obj = Foo()
4 print(type(obj))
5 print(type(Foo))<br><br>###<br><class '__main__.Foo'><br><class 'type'>

 所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象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 = name
 6         self.age = age
 7  
 8     def talk(self):
 9         print('%s is talking ' % self.name)
10 # print(Chinese)
11 obj = Chinese('qiuma',18)
12 print(obj, obj.name, obj.age)
13  
14 ###
15 <__main__.Chinese object at 0x0150C490> qiuma 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=name
19     self.age=age
20 def talk(self):
21     print('%s is talking' %self.name)
22 """
# A没有继承B,但是A内super会基于C.mro()继续往后找
class  A:
     def  f1( self ):
         print ( 'from A' )
         super ().f1()
class  B:
     def  f1( self ):
         print ( 'from B' )
class  C(A,B):
     pass
 
print (C.mro())
=  C()
c.f1()   # 打印结果:from B
 
"""
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
from A
from B
"""

猜你喜欢

转载自www.cnblogs.com/herosyuan/p/10068450.html
今日推荐