Python 面向对象初识

面向过程VS面向对象

概述

面向过程的程序设计的核心是过程(流水式思维) ,  过程即解决问题的步骤,面向过程的设计就好比精心设计好一天流水线,考虑周全什么时候处理什么东西

优点是: 极大的见地了写程序的复杂度,只需要顺着要执行的步骤,堆代码即可

缺点是 : 一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身

应用场景: 一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

面向对象的程序设计的核心是对象(上帝思维),  要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来

面向对象的程序设计  

优点是: 解决了程序的扩展性. 对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物的参数的特征和技能修改都很容易.

缺点是: 可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果.

应用场景: 需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方

总结 : 

  面向过程: 根据业务逻辑从上到下写垒代码

  函数式:  将某功能代码封装到函数中,日后便无需重复编写, 仅调用函数即可

  面向对象: 对函数进行分类和封装,让开发"更快更好更强"

创建类和对象             

面向对象编程是一种编程方式,此编程方式的落地需要使用"类"和"对象"来实现,所以,面向对象编程其实就是对"类"和"对象"的使用.

类就是一个模板,模板里可以包含多个函数,函数里实现一些功能

对象则是根据模板创建的实例,通过实例对象可以执行类中的函数

class 是关键字  ,   表示类

创建对象 ,  类名称后 加括号即可

ps: 类中的函数第一个参数必须是self  
  类定义的函数叫做  "方 法"

创建类

 1 # 创建类
 2 class Foo:
 3 
 4     def bar(self):
 5         print("bar函数")
 6 
 7     def Hello(self,name):
 8         print(name)
 9 
10 #  根据类Foo创建对象obj
11 
12 obj = Foo()
13 obj.bar()  # 执行bar函数
14 obj.Hello("和顺")  #执行Hello 函数

面向对象三大特性           

面向对象的三大特性是指 : 封装 ,  继承 和 多态 

一  . 封装  

封装 , 顾名思义就是将内容封装到某个地方, 以后再去调用被封装在某处的内容.

所以, 在使用面向对象的封装特性时  需要:

  将内容封装到某处

  从某处调用被封装的内容

第一步   :    将内容封装到某处

self 是一个形式参数 , 当执行 obj1 = Foo("heshun",18)时,self 等于obj1

          当执行obj2 = Foo("liuxin",16)时 , self 等于 obj2

所以 , 内容其实被封装到了对象 obj1 和 obj2 中 , 每个对象都有name 和 age 属性   在内存里类似于下图来保存.

 第二步 : 从某处调用被封装的内容

调用被封装的内容时 , 有两种情况:

    通过对象直接调用

    通过self 间接调用

1 ,  通过对象 直接调用被封装的内容

   上图展示了对象  obj1  和 obj2  在内存中保存的方式, 根据处保存格式可以如此调用被封装的内容:  对象,  属性名

 1 class Foo:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5 
 6 obj1 = Foo("heshun",18)
 7 print(obj1.name)   # 直接调用obj1 对象的name属性
 8 print(obj1.age)   # 直接调用obj1 对象的age属性
 9 
10 obj2 = Foo("laifu",15)
11 print(obj2.name)  #直接调用obj2 对象的name属性
12 print(obj2.age)  #直接调用obj2 对象的age属性

 2 , 通过self 间接调用被封装的内容

执行类中的方法时, 需要通过self 间接调用被封装的内容

 1 class Foo:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5 
 6     def func(self):
 7         print(self.name)
 8         print(self.age)
 9 
10 obj1 = Foo("heshun",18)
11 obj1.func()     #  Python默认会将obj1传给self参数,  即obj1.func(obj1),   所以, 此时方法内部的self = obj1  即self.name是"heshun"  self.age 是18
12 
13 obj2 = Foo("xin",18)
14 obj2.func()     #  Python默认会将obj2传给self参数,  即obj2.func(obj1),   所以, 此时方法内部的self = obj2  即self.name是"xin"  self.age 是18

 综上所诉 , 对于面向对象的封装来说,其实就是使用构造方法将内容装到 对象  中 , 然后通过对象直接或者self间接获取被封装的内容

 二  ,继承

 继承 , 面向对象中的继承和现实生活中的继承相同, 即 子可以继承父的内容

例如:

  猫可以:喵喵叫、吃、喝、拉、撒

  狗可以:汪汪叫、吃、喝、拉、撒

如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:

 1 class 猫:
 2 
 3     def 喵喵叫(self):
 4         print '喵喵叫'
 5 
 6     def 吃(self):
 7         # do something
 8 
 9     def 喝(self):
10         # do something
11 
12     def 拉(self):
13         # do something
14 
15     def 撒(self):
16         # do something
17 
18 class 狗:
19 
20     def 汪汪叫(self):
21         print '喵喵叫'
22 
23     def 吃(self):
24         # do something
25 
26     def 喝(self):
27         # do something
28 
29     def 拉(self):
30         # do something
31 
32     def 撒(self):
33         # do something
伪代码

上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:

  动物:吃、喝、拉、撒

     猫:喵喵叫(猫继承动物的功能)

     狗:汪汪叫(狗继承动物的功能)

 1 class 动物:
 2 
 3     def 吃(self):
 4         # do something
 5 
 6     def 喝(self):
 7         # do something
 8 
 9     def 拉(self):
10         # do something
11 
12     def 撒(self):
13         # do something
14 
15 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
16 class 猫(动物):
17 
18     def 喵喵叫(self):
19         print '喵喵叫'
20         
21 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
22 class 狗(动物):
23 
24     def 汪汪叫(self):
25         print '喵喵叫'
伪代码
 1 class Animal:
 2 
 3     def eat(self):
 4         print "%s 吃 " %self.name
 5 
 6     def drink(self):
 7         print "%s 喝 " %self.name
 8 
 9     def shit(self):
10         print "%s 拉 " %self.name
11 
12     def pee(self):
13         print "%s 撒 " %self.name
14 
15 
16 class Cat(Animal):
17 
18     def __init__(self, name):
19         self.name = name
20         self.breed = ''
21 
22     def cry(self):
23         print '喵喵叫'
24 
25 class Dog(Animal):
26     
27     def __init__(self, name):
28         self.name = name
29         self.breed = ''
30         
31     def cry(self):
32         print '汪汪叫'
33         
34 
35 # ######### 执行 #########
36 
37 c1 = Cat('小白家的小黑猫')
38 c1.eat()
39 
40 c2 = Cat('小黑的小白猫')
41 c2.drink()
42 
43 d1 = Dog('胖子家的小瘦狗')
44 d1.eat()
代码实例

所以 , 对于面向对象的继承来说, 其实就是将多个类共有的方法提取到父类中, 子类仅需继承父类而不必一一实现每个方法

父类 又名  基类       子类 又名  派生类

用继承的方法实现上述阿猫阿狗的功能  如下:

 1 class Animal:
 2 
 3     def eat(self):
 4         print "%s 吃 " %self.name
 5 
 6     def drink(self):
 7         print "%s 喝 " %self.name
 8 
 9     def shit(self):
10         print "%s 拉 " %self.name
11 
12     def pee(self):
13         print "%s 撒 " %self.name
14 
15 
16 class Cat(Animal):
17 
18     def __init__(self, name):
19         self.name = name
20         self.breed = ''
21 
22     def cry(self):
23         print '喵喵叫'
24 
25 class Dog(Animal):
26     
27     def __init__(self, name):
28         self.name = name
29         self.breed = ''
30         
31     def cry(self):
32         print '汪汪叫'
33         
34 
35 # ######### 执行 #########
36 
37 c1 = Cat('小白家的小黑猫')
38 c1.eat()
39 
40 c2 = Cat('小黑的小白猫')
41 c2.drink()
42 
43 d1 = Dog('胖子家的小瘦狗')
44 d1.eat()
代码实例

多继承 

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

      

    当类是经典类时,多继承情况下,会按照深度优先方式查找

    当类是新式类时,多继承情况下,会按照广度优先方式查找

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

         

 1 class D:
 2 
 3     def bar(self):
 4         print 'D.bar'
 5 
 6 
 7 class C(D):
 8 
 9     def bar(self):
10         print 'C.bar'
11 
12 
13 class B(D):
14 
15     def bar(self):
16         print 'B.bar'
17 
18 
19 class A(B, C):
20 
21     def bar(self):
22         print 'A.bar'
23 
24 a = A()
25 # 执行bar方法时
26 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
27 # 所以,查找顺序:A --> B --> D --> C
28 # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
29 a.bar()
经典类多继承
 1 class D(object):
 2 
 3     def bar(self):
 4         print 'D.bar'
 5 
 6 
 7 class C(D):
 8 
 9     def bar(self):
10         print 'C.bar'
11 
12 
13 class B(D):
14 
15     def bar(self):
16         print 'B.bar'
17 
18 
19 class A(B, C):
20 
21     def bar(self):
22         print 'A.bar'
23 
24 a = A()
25 # 执行bar方法时
26 # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
27 # 所以,查找顺序:A --> B --> C --> D
28 # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
29 a.bar()
新式类多继承

经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错

新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错

注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

多态   

Python 不支持java 和 C# 这一类强类型语言中多态的写法  ,   它本身就是原生态,  其崇拜""鸭子""类型

 1 class F1:
 2     pass
 3 
 4 
 5 class S1(F1):
 6 
 7     def show(self):
 8         print 'S1.show'
 9 
10 
11 class S2(F1):
12 
13     def show(self):
14         print 'S2.show'
15 
16 
17 # 由于在Java或C#中定义函数参数时,必须指定参数的类型
18 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
19 # 而实际传入的参数是:S1对象和S2对象
20 
21 def Func(F1 obj):
22     """Func函数需要接收一个F1类型或者F1子类的类型"""
23     
24     print obj.show()
25     
26 s1_obj = S1()
27 Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
28 
29 s2_obj = S2()
30 Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
Python伪代码实现Java或C#的多态
 1 class F1:
 2     pass
 3 
 4 
 5 class S1(F1):
 6 
 7     def show(self):
 8         print 'S1.show'
 9 
10 
11 class S2(F1):
12 
13     def show(self):
14         print 'S2.show'
15 
16 def Func(obj):
17     print obj.show()
18 
19 s1_obj = S1()
20 Func(s1_obj) 
21 
22 s2_obj = S2()
23 Func(s2_obj) 
Python “鸭子类型”

总结 :

  面向对象是一种编程方式, 此编程方式的实现是基于对类和对象的使用

  类是一个模板,模板中包含多个""函数"" 使用

  对象,根据模板创建的实例(即 : 对象),实例用于调用被包装在类中的函数

  面向对象三大特征: 封装,继承,和多态

在Python中开发中  全部使用面向对象 , 或 面向对象和函数式混合使用

类和对象在内存中的保存

类以及类中的方法在内存中只有一份,而根据类的创建的每一个对象都在内存中需要存一份,  大致如下图

如上图所示,根据类创建对象时 ,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针, 该值指向当前对象的类

  当通过obj1 执行[方法一] 时 , 过程如下:

  1 根据当前对象中的类对象指针  找到类中的方法

  2 将对象obj1当做参数传给 方法的第一个参数self

猜你喜欢

转载自www.cnblogs.com/heshun/p/9543722.html