一 三大编程范式
编程范式即编程的方法论,标识一种编程风格
三大编程范式:
1.面向过程编程
2.函数式编程
3.面向对象编程
二 面向对象编程与面向对象设计
1.面向对象设计
面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计
#相当于:类 def dog(name,gender,type): #狗的动作 def jiao(dog): print('一条狗[%s],汪汪汪' % dog['name']) def chi_shi(dog): print('一条[%s] 正在吃肉' % dog['type']) #狗的属性 def init(name,gender,type): dog1 = { 'name':name, 'gender':gender, 'type':type, 'jiao':jiao, #动作 'chi_shi':chi_shi, #动作 } return dog1 return init(name,gender,type) d1=dog('元吴','母','中华犬') d2=dog('alex','母','藏敖') print(d1) print(d2) d1['jiao'](d1) d2['chi_shi'](d2)
什么是类?
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念。
什么是对象?
对象:就是基于类而创建的一个具体的事物(指具体存在的),也是特征和动作整合到一起。
2.面向对象编程
面向对象编程(object-oriented programming):用定义类+实例/对象的方式去实现面向对象的设计
#用面向对象编程独有的语法class去实现面向对象设计 class Dog: def __init__(self,name,gender,type): #实例化的过程,就是运行__init__ self.name=name self.gender=gender self.type=type def bark(self): print('一条名字为[%s]的[%s],狂吠不止' %(self.name,self.type)) def yao_ren(self): print('[%s]正在咬人' %(self.name)) def chi_shi(self): print('[%s]正在吃屎' %(self.type)) dog1=Dog('alex','female','京巴') dog2=Dog('wupeiqi','female','腊肠') dog3=Dog('yuanhao','female','藏獒') dog1.bark() dog2.yao_ren() dog3.chi_shi()
小结:
在Python中,类和 OOP 都不是日常编程所必需的。尽管它从一开始设计就是面向对象的,并且结构上支持 OOP,但 Python 没有限定或要求你在你的应用中写 OOP 的代码。
用面向对象语言写程序和一个程序的设计是面向对象的,两者就是两码事。
三 类和对象
1.什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)。
2.什么叫对象:睁开眼,你看到的一切的事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)
(铅笔是对象,人是对象,房子是对象,狗是对象,alex是对象,配齐是对象,元昊是对象)
3.类与对象的关系:对象都是由类产生的,上帝造人,上帝首先有一个造人的模板,这个模板即人的类,然后上帝根据类的定义来生产一个个的人
4.什么叫实例化:由类生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例=对象)
四 类的相关知识
1、初识类
在python中声明函数与声明类很相似。
声明函数
1
2
3
|
1 def functionName(args): 2 '函数文档字符串' 3 函数体 |
声明类
''' class 类名: '类的文档字符串' 类体 ''' #我们创建一个类 class Data: pass #用类Data实例化出一个对象d1 d1=Data()
经典类与新式类
大前提: 1.只有在python2中才分新式类和经典类,python3中统一都是新式类 2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类 3.所有类甭管是否显式声明父类,都有一个默认继承object父类(讲继承时会讲,先记住) 在python2中的区分 经典类: class 类名: pass 经典类: class 类名(父类): pass 在python3中,上述两种定义方式全都是新式类
示例:
#经典类(python2.7) class Chinese: '这是一个中国人的类' pass print(Chinese) #新式类(python3.0) #实例化到底干了什么? p1=Chinese() #实例化 print(p1)
2、属性
类是用来描述一类事物,类的对象指的是这一类事物中的一个个体
是事物就要有属性,属性分为
1:数据属性:就是变量
2:函数属性:就是函数,在面向对象里通常称为方法
注意:类和对象均用点来访问自己的属性
类的属性
理论:数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用。
类的数据属性
#定义一个中文人的类,然后在类中定义一个类的属性,政府是共产堂,这样,只要是中文人他们的党永远都是共产堂 #类属性又称为静态变量,或者是静态数据。这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。 #如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字。 class Chinese: government='共产堂' print(Chinese.government)
类的函数属性(又称为方法)
class Chinese: government='共产_党' def sui_di_tu_tan(): print('90%的中国人都喜欢随地吐痰') def cha_dui(self): print('一个中国人-->%s<--插到了前面' %self) Chinese.sui_di_tu_tan() person1_obj='alex' Chinese.cha_dui(person1_obj) #带参函数,所以调用时需要传入参数,将'alex'传给self
3、面向对象的三大属性
面向对象的三大特性是指:封装、继承和多态。
一、封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
- 将内容封装到某处
- 从某处调用被封装的内容
self 是一个形式参数,当执行 obj1 = Foo('wupeiqi', 18 ) 时,self 等于 obj1
当执行 obj2 = Foo('alex', 78 ) 时,self 等于 obj2
所以,内容其实被封装到了对象 obj1 和 obj2 中,每个对象中都有 name 和 age 属性,在内存里类似于下图来保存。
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种情况:
- 通过对象直接调用
- 通过self间接调用
1、通过对象直接调用被封装的内容
上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Foo: def __init__( self , name, age): self .name = name self .age = age obj1 = Foo( 'wupeiqi' , 18 ) print obj1.name # 直接调用obj1对象的name属性 print obj1.age # 直接调用obj1对象的age属性 obj2 = Foo( 'alex' , 73 ) print obj2.name # 直接调用obj2对象的name属性 print obj2.age # 直接调用obj2对象的age属性 |
2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Foo: def __init__( self , name, age): self .name = name self .age = age def detail( self ): print self .name print self .age obj1 = Foo( 'wupeiqi' , 18 ) obj1.detail() # Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18 obj2 = Foo( 'alex' , 73 ) obj2.detail() # Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78 |
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。
二、继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
那么问题又来了,多继承呢?
- 是否可以继承多个类
- 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
1、Python的类可以继承多个类,Java和C#中则只能继承一个类
2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
此外,新式类和经典类还有其他区别。看下面这段代码
class person(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex class Teacher(person): def __init__(self,name,age,sex,salary,course): person.__init_(self,name,age,sex) #经典类 super(Teacher,self).__init__(name,age,sex) #新式类
代码中Teacher类继承了person类,在构造函数中使用了两种继承的方式。
classical vs new style:
- 经典类:深度优先
- 新式类:广度优先
- super()用法
三、多态
多态是为实现接口的重用性,多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。python是不支持多态的,其实python也无需支持多态。因为python的语言类型,python可以使用“鸭子类型”来实现多态。我的理解就是定义新的函数,通过函数改变调用的方式。
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象 def Func(F1 obj): """Func函数需要接收一个F1类型或者F1子类的类型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show Python伪代码实现Java或C#的多态
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj) Python “鸭子类型”
4、总结
以上就是本节对于面向对象初级知识的介绍,总结如下:
- 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用
- 类 是一个模板,模板中包装了多个“函数”供使用
- 对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数
- 面向对象三大特性:封装、继承和多态
类和对象在内存中是如何保存?
类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:
如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值指向当前对象的类。
当通过 obj1 执行 【方法一】 时,过程如下:
- 根据当前对象中的 类对象指针 找到类中的方法
- 将对象 obj1 当作参数传给 方法的第一个参数 self