一、面向对象概述
如今主流的软件开发思想有两种:一个是面向过程,另一个是面向对象。面向过程出现得较早,典型代表为C语言,开发中小型项目的效率很高,但是很难适用于如今主流的大中型项目开发场景。面向对象则出现得更晚一些,典型代表为Java或C++等语言,更加适合用于大型开发场景。两种开发思想各有长短。
对于面向过程的思想: 需要实现一个功能的时候,看重的是开发的步骤和过程,每一个步骤都需要自己亲力亲为,需要自己编写代码(自己来做)
对于面向对象的思想:当需要实现一个功能的时候,看重的并不是过程和步骤,而是关心谁帮我做这件事(偷懒,找人帮我做)
面向对象的三大特征有:封装性、继承性、多态性。
1.1 面向过程与面向对象
二、类与对象
面向对象编程的2个非常重要的概念:类和对象。
我们学习编程语言,就是为了模拟现实世界中的事物,实现信息化来提高工作效率。例如银行的业务系统、超市的结账系统等,都是如此。
面向对象的语言当中,“类”就是用来模拟现实事物的。
那么模拟现实世界的事物通常从两方面模拟:
- 属性:事物的特征描述信息,用于描述某个特征“是什么”。
- 行为:事物的能力行动方案,用于说明事物“能做什么”。
类中也有属性、行为两个组成部分,而“对象”是类的具体实例。例如:
- 类:抽象的,是一张“手机设计图”。
- 对象:具体的,是一个“真正的手机实例”。
2.1 初识类
python中一切皆为对象,类型的本质就是类。
>>> dict
#
类型dict就是类dict
<
class
'
dict
'
>
>>> d=dict(name=
'
eva
'
)
#
实例化
>>> d.pop(
'
name
'
)
#
向d发一条消息,执行d的方法pop
'
eva
'
2.2 类的定义
定义一个类,格式如下:
class
类名:
方法列表
定义一个People类:
1
#
经典类 python2中才有的
2
#
class People:
3
#
pass
4
5
6
#
新式类 python3中默认都是新式类
7
class
People(object):
8
9
def
walk(self):
10
print
(
"
walk....
"
)
说明:
- 定义类的形式有两种,新式类和经典类,python3中默认都是新式类,python2中有新式类和经典类两种
- object类是python中所有类的最顶级父类
- 类名的命名方式按照"大驼峰命名法"
- walk 是一个实例方法,第一个参数一般是self,表示实例对象本身,当然了可以将self换为其它的名字,其作用是一个变量 这个变量指向了实例对象
2.2.1 对象的创建
python中,可以根据已经定义的类去创建出一个或多个对象。
创建对象的格式为:
对象名1 =
类名()
对象名2
=
类名()
对象名3
= 类名()
1
class
Hero(object):
#
新式类定义形式
2
"""
info 是一个实例方法,类对象可以调用实例方法,实例方法的第一个参数一定是self
"""
3
def
info(self):
4
"""
当对象调用实例方法时,Python会自动将对象本身的引用做为参数,
5
传递到实例方法的第一个参数self里
"""
6
print
(self)
7
print
(
"
self各不同,对象是出处。
"
)
8
9
10
#
Hero这个类 实例化了一个对象 taidamier(泰达米尔)
11
h =
Hero()
12
13
#
对象调用实例方法info(),执行info()里的代码
14
#
. 表示选择属性或者方法
15
h.info()
16
17
print
(h)
#
打印对象,则默认打印对象在内存的地址,结果等同于info里的print(self)
18
print
(id(h))
#
id(h) 则是内存地址的十进制形式表示
2.2.2 __init__实例方法
实例化:类名加括号就是实例化(创建对象),会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
1
class
Hero(object):
2
"""
定义了一个英雄类,可以移动和攻击
"""
3
#
Python 的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法,__init__()就是一个魔法方法,通常用来做属性初始化 或 赋值 操作。
4
#
如果类面没有写__init__方法,Python会自动创建,但是不执行任何操作,
5
#
如果为了能够在完成自己想要的功能,可以自己定义__init__方法,
6
#
所以一个类里无论自己是否编写__init__方法 一定有__init__方法。
7
8
def
__init__
(self):
9
"""
方法,用来做变量初始化 或 赋值 操作,在类实例化对象的时候,会被自动调用
"""
10
self.name =
"
aa
"
#
姓名
11
self.hp = 2600
#
生命值
12
self.atk = 450
#
攻击力
13
self.armor = 200
#
护甲值
14
15
def
move(self):
16
"""
实例方法
"""
17
print
(
"
正在前往事发地点...
"
)
18
19
def
attack(self):
20
"""
实例方法
"""
21
print
(
"
发出了一招强力的普通攻击...
"
)
22
23
24
hero =
Hero()
25
print
(hero.name)
26
hero.move()
说明:
-
__init__()方法,在创建一个对象时默认被调用,不需要手动调用 - __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。
带参数的__init__方法:
1
class
Person(object):
2
3
def
__init__
(self, name, age):
4
self.name =
name # 实例属性
5
self.age =
age
6
7
def
play(self):
8
print
(
"
愉快的玩耍
"
)
9
10
def
info(self):
11
print
(
"
姓名:
"
, self.name)
#
在方法内使用self获取对象属性
12
print
(
"
年龄:
"
, self.age)
13
14
15
#
实例化对象,参数传递到__init__方法里
16
p1 = Person(
"
aa
"
, 12
)
17
p1.info()
说明:
- 通过一个类,可以创建多个对象,就好比 通过一个模具创建多个实体一样
-
__init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y) - 在类内部获取 属性 和 实例方法,通过self获取
- 在类外部获取 属性 和 实例方法,通过对象名获取
- 如果一个类有多个对象,每个对象的属性是各自保存的,都有各自独立的地址
- 但是实例方法是所有对象共享的,只占用一份内存空间。类会通过self来判断是哪个对象调用了实例方法。
2.2.3 类的属性
1
class
Person:
#
定义一个人类
2
role =
'
person
'
#
人的角色属性都是人
3
4
def
walk(self):
#
人都可以走路,也就是有一个走路方法
5
print
(
"
person is walking...
"
)
6
7
8
print
(Person.role)
#
查看人的role属性
9
print
(Person.walk)
#
引用人的走路方法,注意,这里不是在调用
10
11
p =
Person()
12
p.role =
"
aa
"
13
print
(p.role)
#
实例属性会屏蔽掉同名的类属性 aa
14
print
(Person.role)
#
person
如果需要在类外修改
类属性
,必须通过
类对象
去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的
实例属性
,这种方式修改的是
实例属性
,不会影响到
类属性
,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是
实例属性
,除非删除了该
实例属性
。
2.2.4 类属性的补充
一:我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.
__dict__
:查出的是一个字典,key为属性名,value为属性值
二:特殊的类属性
类名.
__name__
#
类的名字(字符串)
类名.
__doc__
#
类的文档字符串
类名.
__bases__
#
类所有父类构成的元组(在讲继承时会讲)
类名.
__dict__
#
类的字典属性
类名.
__module__
#
类定义所在的模块
类名.
__class__
#
实例对应的类(仅新式类中)
2.2.5 对象之间的交互
1
#
egon大战哈士奇
2
class
Person:
#
定义一个人类
3
role =
'
person
'
#
人的角色属性都是人
4
5
def
__init__
(self, name, aggressivity, life_value):
6
self.name = name
#
每一个角色都有自己的昵称;
7
self.aggressivity = aggressivity
#
每一个角色都有自己的攻击力;
8
self.life_value = life_value
#
每一个角色都有自己的生命值;
9
10
def
attack(self,dog):
11
#
人可以攻击狗,这里的狗也是一个对象。
12
#
人攻击狗,那么狗的生命值就会根据人的攻击力而下降
13
dog.life_value -=
self.aggressivity
14
15
16
class
Dog:
#
定义一个狗类
17
role =
'
dog
'
#
狗的角色属性都是狗
18
19
def
__init__
(self, name, breed, aggressivity, life_value):
20
self.name = name
#
每一只狗都有自己的昵称;
21
self.breed = breed
#
每一只狗都有自己的品种;
22
self.aggressivity = aggressivity
#
每一只狗都有自己的攻击力;
23
self.life_value = life_value
#
每一只狗都有自己的生命值;
24
25
def
bite(self,people):
26
#
狗可以咬人,这里的狗也是一个对象。
27
#
狗咬人,那么人的生命值就会根据狗的攻击力而下降
28
people.life_value -=
self.aggressivity
29
30
31
egg = Person(
'
egon
'
,10,1000)
#
创造了一个实实在在的人egg
32
ha2 = Dog(
'
二愣子
'
,
'
哈士奇
'
,10,1000)
#
创造了一只实实在在的狗ha2
33
print
(ha2.life_value)
#
看看ha2的生命值
34
egg.attack(ha2)
#
egg打了ha2一下
35
print
(ha2.life_value)
#
ha2掉了10点血
1
#
一个简单的例子帮你理解面向对象
2
from
math
import
pi
3
4
5
class
Circle:
6
'''
7
定义了一个圆形类;
8
提供计算面积(area)和周长(perimeter)的方法
9
'''
10
def
__init__
(self,radius):
11
self.radius =
radius
12
13
def
area(self):
14
return
pi * self.radius *
self.radius
15
16
def
perimeter(self):
17
return
2 * pi *
self.radius
18
19
20
circle = Circle(10)
#
实例化一个圆
21
area1 = circle.area()
#
计算圆面积
22
per1 = circle.perimeter()
#
计算圆周长
23
print
(area1, per1)
#
打印圆面积和周长
2.3 类命名空间与对象、实例的命名空间
-
静态属性就是直接在类中定义的变量
-
动态属性就是定义在类中的方法
1
class
Person(object):
2
3
role =
"
person
"
4
5
def
__init__
(self):
6
pass
7
8
def
walk(self):
9
pass
10
11
12
p1 =
Person()
13
p2 =
Person()
14
15
print
(id(Person.role))
16
print
(id(p1.role))
17
print
(id(p2.role))
18
19
print
(Person.walk)
20
print
(p1.walk)
21
print
(p2.walk)
22
23
#
运行结果:
24
#
2427822463496
25
#
2427822463496
26
#
2427822463496
27
#
28
#
<__main__.Person object at 0x00000235457FC7B8>>
29
#
<__main__.Person object at 0x00000235457FC390>>
说明:
- 类的数据数据是共享给所有对象使用的
- 类的动态属性是绑定到所有对象的
- 创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
- 如:obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
2.4 面向对象的组合用法
1
"""
2
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
3
这个时候,我们就首先实现一个圆形类,计算一个圆的周长和面积。然后在"环形类"中组合圆形的实例作为自己的属性来用
4
"""
5
6
from
math
import
pi
7
8
9
class
Circle:
10
'''
11
定义了一个圆形类;
12
提供计算面积(area)和周长(perimeter)的方法
13
'''
14
def
__init__
(self,radius):
15
self.radius =
radius
16
17
def
area(self):
18
return
pi * self.radius *
self.radius
19
20
def
perimeter(self):
21
return
2 * pi *
self.radius
22
23
24
circle = Circle(10)
#
实例化一个圆
25
area1 = circle.area()
#
计算圆面积
26
per1 = circle.perimeter()
#
计算圆周长
27
print
(area1, per1)
#
打印圆面积和周长
28
29
30
class
Ring:
31
'''
32
定义了一个圆环类
33
提供圆环的面积和周长的方法
34
'''
35
def
__init__
(self,radius_outside,radius_inside):
36
self.outsid_circle =
Circle(radius_outside)
37
self.inside_circle =
Circle(radius_inside)
38
39
def
area(self):
40
return
self.outsid_circle.area() -
self.inside_circle.area()
41
42
def
perimeter(self):
43
return
self.outsid_circle.perimeter() +
self.inside_circle.perimeter()
44
45
46
ring = Ring(10, 5)
#
实例化一个环形
47
print
(ring.perimeter())
#
计算环形的周长
48
print
(ring.area())
#
计算环形的面积
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程
1
class
BirthDate:
2
def
__init__
(self,year,month,day):
3
self.year=
year
4
self.month=
month
5
self.day=
day
6
7
8
class
Couse:
9
def
__init__
(self,name,price,period):
10
self.name=
name
11
self.price=
price
12
self.period=
period
13
14
15
class
Teacher:
16
def
__init__
(self,name,gender,birth,course):
17
self.name=
name
18
self.gender=
gender
19
self.birth=
birth
20
self.course=
course
21
22
def
teach(self):
23
print
(
'
teaching
'
)
24
25
26
p1=Teacher(
'
egon
'
,
'
male
'
,
27
BirthDate(
'
1995
'
,
'
1
'
,
'
27
'
),
28
Couse(
'
python
'
,
'
28000
'
,
'
4 months
'
)
29
)
30
31
32
print
(p1.birth.year,p1.birth.month,p1.birth.day)
33
34
35
print
(p1.course.name,p1.course.price,p1.course.period)
36
'''
37
运行结果:
38
27
39
python 28000 4 months
40
'''
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好.
三、面向对象的三大特性
3.1 继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。
Python的继承分为单继承和多继承:
class
ParentClass1(object):
#
定义父类
pass
class
ParentClass2(object):
#
定义父类
pass
class
SubClass1(ParentClass1):
#
单继承,基类是ParentClass1,派生类是SubClass
pass
class
SubClass2(ParentClass1,ParentClass2):
#
python支持多继承,用逗号分隔开多个继承的类
pass
查看继承:
>>> SubClass1.
__bases__
#
__bases__则是查看所有继承的父类
(<
class
'
__main__.ParentClass1
'
>
,)
>>> SubClass2.
__bases__
(
<
class
'
__main__.ParentClass1
'
>, <
class
'
__main__.ParentClass2
'
>)
ps:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.
__bases__
(
<
class
'
object
'
>
,)
>>> ParentClass2.
__bases__
(
<
class
'
object
'
>,)
3.1.1 单继承
1
#
定义一个Master类
2
class
Master(object):
3
def
__init__
(self):
4
#
属性
5
self.kongfu =
"
古法煎饼果子配方
"
6
7
#
实例方法
8
def
make_cake(self):
9
print
(
"
按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
10
11
12
#
定义Prentice类,继承了 Master,则Prentice是子类,Master是父类。
13
class
Prentice(Master):
14
#
子类可以继承父类所有的属性和方法,哪怕子类没有自己的属性和方法,也可以使用父类的属性和方法。
15
pass
16
17
#
laoli = Master()
18
#
print(laoli.kongfu)
19
#
laoli.make_cake()
20
21
22
damao = Prentice()
#
创建子类实例对象
23
print
(damao.kongfu)
#
子类对象可以直接使用父类的属性
24
damao.make_cake()
#
子类对象可以直接使用父类的方法
注:虽然子类没有定义
__init__
方法初始化属性,也没有定义实例方法,但是父类有。所以只要创建子类的对象,就默认执行了那个继承过来的
__init__
方法
3.1.2 多继承
1
class
Master(object):
2
def
__init__
(self):
3
self.kongfu =
"
古法煎饼果子配方
"
#
实例变量,属性
4
5
def
make_cake(self):
#
实例方法,方法
6
print
(
"
[古法] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
7
8
def
dayandai(self):
9
print
(
"
师傅的大烟袋..
"
)
10
11
12
class
School(object):
13
def
__init__
(self):
14
self.kongfu =
"
现代煎饼果子配方
"
15
16
def
make_cake(self):
17
print
(
"
[现代] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
18
19
def
xiaoyandai(self):
20
print
(
"
学校的小烟袋..
"
)
21
22
23
#
class Prentice(School, Master): # 多继承,继承了多个父类(School在前)
24
#
pass
25
26
#
damao = Prentice()
27
#
print(damao.kongfu)
28
#
damao.make_cake()
29
#
damao.dayandai()
30
#
damao.xiaoyandai()
31
32
33
class
Prentice(Master, School):
#
多继承,继承了多个父类(Master在前)
34
pass
35
36
37
damao =
Prentice()
38
print
(damao.kongfu)
#
执行Master的属性
39
damao.make_cake()
#
执行Master的实例方法
40
41
#
子类的魔法属性__mro__决定了属性和方法的查找顺序
42
print
(Prentice.
__mro__
)
43
44
damao.dayandai()
#
不重名不受影响
45
damao.xiaoyandai()
说明:
- 多继承可以继承多个父类,也继承了所有父类的属性和方法
- 注意:如果多个父类中有同名的 属性和方法,则默认使用第一个父类的属性和方法(根据类的魔法属性mro的顺序来查找)
- 多个父类中,不重名的属性和方法,不会有任何影响。
Python2中多继承中的问题:
class
A(object):
def
test(self):
print
(
'
from A
'
)
class
B(A):
def
test(self):
print
(
'
from B
'
)
class
C(A):
def
test(self):
print
(
'
from C
'
)
class
D(B):
def
test(self):
print
(
'
from D
'
)
class
E(C):
def
test(self):
print
(
'
from E
'
)
class
F(D,E):
#
def test(self):
#
print('from F')
pass
f1
=
F()
f1.test()
print
(F.
__mro__
)
#
只有新式才有这个属性可以查看线性列表,经典类没有这个属性
#
新式类继承顺序:F->D->B->E->C->A
#
经典类继承顺序:F->D->B->A->E->C
#
python3中统一都是新式类
#
pyhon2中才分新式类与经典类
Python的继承原理:对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
3.1.3 派生
3.1.3.1子类重写父类的同名属性和方法
1 class
Master(object):
2
3 def __init__
(self):
4 self.kongfu = "古法煎饼果子配方"
5
6 def
make_cake(self):
7 print("[古法] 按照 <%s> 制作了一份煎饼果子..." %
self.kongfu)
8
9
10 class
School(object):
11 def __init__
(self): 12 self.kongfu = "现代煎饼果子配方" 13 14 def
make_cake(self): 15 print("[现代] 按照 <%s> 制作了一份煎饼果子..." %
self.kongfu) 16 17 18 class Prentice(School, Master): # 多继承,继承了多个父类 19 20 def __init__
(self): 21 self.kongfu = "猫氏煎饼果子配方" 22 23 def
make_cake(self): 24 print("[猫氏] 按照 <%s> 制作了一份煎饼果子..." %
self.kongfu) 25 26 27 # 如果子类和父类的方法名和属性名相同,则默认使用子类的 28 # 叫 子类重写父类的同名方法和属性 29 damao =
Prentice() 30 print(damao.kongfu) # 子类和父类有同名属性,则默认使用子类的 31 damao.make_cake() # 子类和父类有同名方法,则默认使用子类的 32 33 # 子类的魔法属性__mro__决定了属性和方法的查找顺序 34 print(Prentice.__mro__)
3.1.3.2 子类调用父类的同名方法和属性
1
class
Master(object):
2
def
__init__
(self):
3
self.kongfu =
"
古法煎饼果子配方
"
#
实例变量,属性
4
5
def
make_cake(self):
#
实例方法,方法
6
print
(
"
[古法] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
7
8
9
class
School(object):
10
def
__init__
(self):
11
self.kongfu =
"
现代煎饼果子配方
"
12
13
def
make_cake(self):
14
print
(
"
[现代] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
15
16
17
class
Prentice(School, Master):
#
多继承,继承了多个父类
18
19
def
__init__
(self):
20
self.kongfu =
"
猫氏煎饼果子配方
"
21
22
def
make_cake(self):
23
print
(
"
执行子类的__init__方法前,self.kongfu属性:%s
"
%
self.kongfu)
24
self.
__init__
()
#
执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
25
print
(
"
执行子类的__init__方法前,self.kongfu属性:%s
"
%
self.kongfu)
26
print
(
"
[猫氏] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
27
28
#
调用父类方法格式:父类类名.父类方法(self)
29
def
make_old_cake(self):
30
#
不推荐这样访问父类的实例属性,相当于创建了一个新的父类对象
31
#
print("直接调用Master类的kongfu属性:%s" % Master().kongfu)
32
33
#
可以通过执行Master类的__init__方法,来修改self的属性值
34
print
(
"
执行Master类的__init__方法前,self.kongfu属性:%s
"
%
self.kongfu)
35
Master.
__init__
(self)
#
调用了父类Master的__init__方法 self.kongfu = "古法...."
36
print
(
"
执行Master类的__init__方法后,self.kongfu属性:%s
"
%
self.kongfu)
37
Master.make_cake(self)
#
调用父类Master的实例方法
38
39
def
make_new_cake(self):
40
#
不推荐这样访问类的实例属性,相当于创建了一个新的父类对象
41
#
print("直接调用School类的kongfu属性:%s" % School().kongfu)
42
43
#
可以通过执行School类的__init__方法,来修改self的属性值
44
print
(
"
执行School类的__init__方法前,self.kongfu属性:%s
"
%
self.kongfu)
45
School.
__init__
(self)
#
调用了父类School的__init__方法 self.kongfu = "现代...."
46
print
(
"
执行School类的__init__方法后,self.kongfu属性:%s
"
%
self.kongfu)
47
School.make_cake(self)
#
调用父类School的实例方法
48
49
50
#
实例化对象,自动执行子类的__init__方法
51
damao =
Prentice()
52
53
damao.make_cake()
#
调用子类的方法(默认重写了父类的同名方法)
54
55
print
(
"
--
"
* 10
)
56
damao.make_old_cake()
#
进入实例方法去调用父类Master的方法
57
58
print
(
"
--
"
* 10
)
59
damao.make_new_cake()
#
进入实例方法去调用父类School的方法
60
61
print
(
"
--
"
* 10
)
62
damao.make_cake()
#
调用本类的实例方法
3.1.3.3 多层继承
1
class
Master(object):
2
def
__init__
(self):
3
self.kongfu =
"
古法煎饼果子配方
"
4
5
def
make_cake(self):
6
print
(
"
[古法] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
7
8
9
class
School(object):
10
def
__init__
(self):
11
self.kongfu =
"
现代煎饼果子配方
"
12
13
def
make_cake(self):
14
print
(
"
[现代] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
15
16
17
class
Prentice(School, Master):
#
多继承,继承了多个父类
18
19
def
__init__
(self):
20
self.kongfu =
"
猫氏煎饼果子配方
"
21
self.money = 10000
#
亿美金
22
23
def
make_cake(self):
24
self.
__init__
()
#
执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
25
print
(
"
[猫氏] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
26
27
#
调用父类方法格式:父类类名.父类方法(self)
28
def
make_old_cake(self):
29
Master.
__init__
(self)
#
调用了父类Master的__init__方法 self.kongfu = "古法...."
30
Master.make_cake(self)
#
调用了父类Master的实例方法
31
32
def
make_new_cake(self):
33
School.
__init__
(self)
#
调用了父类School的__init__方法 self.kongfu = "现代...."
34
School.make_cake(self)
#
调用父类School的实例方法,
35
36
37
class
PrenticePrentice(Prentice):
#
多层继承
38
pass
39
40
41
pp =
PrenticePrentice()
42
pp.make_cake()
#
调用父类的实例方法
43
pp.make_new_cake()
44
pp.make_old_cake()
45
46
print
(pp.money)
3.1.3.4 super的使用
1
class
Master(object):
2
def
__init__
(self):
3
self.kongfu =
"
古法煎饼果子配方
"
#
实例变量,属性
4
5
def
make_cake(self):
#
实例方法,方法
6
print
(
"
[古法] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
7
8
9
#
父类是 Master类
10
class
School(Master):
11
12
def
__init__
(self):
13
self.kongfu =
"
现代煎饼果子配方
"
14
15
def
make_cake(self):
16
print
(
"
[现代] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
17
super().
__init__
()
#
执行父类的构造方法
18
super().make_cake()
#
执行父类的实例方法
19
20
21
#
父类是 School 和 Master
22
class
Prentice(School, Master):
#
多继承,继承了多个父类
23
24
def
__init__
(self):
25
self.kongfu =
"
猫氏煎饼果子配方
"
26
27
def
make_cake(self):
28
self.
__init__
()
#
执行本类的__init__方法,做属性初始化 self.kongfu = "猫氏...."
29
print
(
"
[猫氏] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
30
31
def
make_all_cake(self):
32
#
方式1. 指定执行父类的方法(代码臃肿)
33
#
School.__init__(self)
34
#
School.make_cake(self)
35
#
36
#
Master.__init__(self)
37
#
Master.make_cake(self)
38
#
39
#
self.__init__()
40
#
self.make_cake()
41
42
#
方法2. super() 带参数版本,只支持新式类
43
#
super(Prentice, self).__init__() # 执行父类的 __init__方法
44
#
super(Prentice, self).make_cake()
45
#
self.make_cake()
46
47
#
方法3. super()的简化版,只支持新式类
48
super().
__init__
()
#
执行父类的 __init__方法
49
super().make_cake()
#
执行父类的 实例方法
50
self.make_cake()
#
执行本类的实例方法
51
52
53
damao =
Prentice()
54
damao.make_cake()
55
damao.make_all_cake()
56
57
#
print(Prentice.__mro__)
注意:如果继承了多个父类,且父类都有同名方法,则默认只执行第一个父类的(同名方法只执行一次,目前super()不支持执行多个父类的同名方法),super时python2.3版本以上才出现的。
3.1.4 抽象类
1
#
一切皆文件
2
import
abc
#
利用abc模块实现抽象类
3
4
5
class
All_file(metaclass=
abc.ABCMeta):
6
all_type =
'
file
'
7
8
@abc.abstractmethod
#
定义抽象方法,无需实现功能
9
def
read(self):
10
'
子类必须定义读功能
'
11
pass
12
13
@abc.abstractmethod
#
定义抽象方法,无需实现功能
14
def
write(self):
15
'
子类必须定义写功能
'
16
pass
17
18
19
#
class Txt(All_file):
20
#
pass
21
#
22
#
t1=Txt() # 报错,子类没有定义抽象方法
23
24
25
class
Txt(All_file):
#
子类继承抽象类,但是必须定义read和write方法
26
def
read(self):
27
print
(
'
文本数据的读取方法
'
)
28
29
def
write(self):
30
print
(
'
文本数据的读取方法
'
)
31
32
33
class
Sata(All_file):
#
子类继承抽象类,但是必须定义read和write方法
34
def
read(self):
35
print
(
'
硬盘数据的读取方法
'
)
36
37
def
write(self):
38
print
(
'
硬盘数据的读取方法
'
)
39
40
41
class
Process(All_file):
#
子类继承抽象类,但是必须定义read和write方法
42
def
read(self):
43
print
(
'
进程数据的读取方法
'
)
44
45
def
write(self):
46
print
(
'
进程数据的读取方法
'
)
47
48
49
wenbenwenjian=
Txt()
50
51
52
yingpanwenjian=
Sata()
53
54
55
jinchengwenjian=
Process()
56
57
58
#
这样大家都是被归一化了,也就是一切皆文件的思想
59
wenbenwenjian.read()
60
yingpanwenjian.write()
61
jinchengwenjian.read()
62
63
64
print
(wenbenwenjian.all_type)
65
print
(yingpanwenjian.all_type)
66
print
(jinchengwenjian.all_type)
3.2 封装
封装的意义:
- 将属性和方法放到一起做为一个整体,然后通过实例化对象来处理;
- 隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了;
- 对类的属性和方法增加 访问权限控制。
私有权限:在属性名和方法名 前面 加上两个下划线 __
- 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问;
- 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问;
- 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用。
3.2.1 私有变量
1
#
其实这仅仅这是一种变形操作
2
#
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
3
4
5
class
A:
6
__N
=0
#
类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
7
def
__init__
(self):
8
self.
__X
=10
#
变形为self._A__X
9
10
def
__foo
(self):
#
变形为_A__foo
11
print
(
'
from A
'
)
12
13
def
bar(self):
14
self.
__foo
()
#
只有在类内部才可以通过__foo的形式访问到.
15
16
17
#
A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形。
3.2.2 私有方法
1
class
Master(object):
2
def
__init__
(self):
3
self.kongfu =
"
古法煎饼果子配方
"
4
def
make_cake(self):
5
print
(
"
[古法] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
6
7
class
Prentice(School, Master):
8
def
__init__
(self):
9
self.kongfu =
"
猫氏煎饼果子配方
"
10
#
私有属性,可以在类内部通过self调用,但不能通过对象访问
11
self.
__money
= 10000
12
13
#
私有方法,可以在类内部通过self调用,但不能通过对象访问
14
def
__print_info
(self):
15
print
(self.kongfu)
16
print
(self.
__money
)
17
18
def
make_cake(self):
19
self.
__init__
()
20
print
(
"
[猫氏] 按照 <%s> 制作了一份煎饼果子...
"
%
self.kongfu)
21
22
def
make_old_cake(self):
23
Master.
__init__
(self)
24
Master.make_cake(self)
25
26
27
def
make_new_cake(self):
28
School.
__init__
(self)
29
School.make_cake(self)
30
31
class
PrenticePrentice(Prentice):
32
pass
33
34
35
damao =
Prentice()
36
#
对象不能访问私有权限的属性和方法
37
#
print(damao.__money)
38
#
damao.__print_info()
39
40
41
pp =
PrenticePrentice()
42
#
子类不能继承父类私有权限的属性和方法
43
print
(pp.
__money
)
44
pp.
__print_info
()
3.2.3 封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
1
#
类的设计者
2
class
Room:
3
def
__init__
(self,name,owner,width,length,high):
4
self.name=
name
5
self.owner=
owner
6
self.
__width
=
width
7
self.
__length
=
length
8
self.
__high
=
high
9
10
def
tell_area(self):
#
对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
11
return
self.
__width
* self.
__length
12
13
14
#
使用者
15
>>> r1=Room(
'
卧室
'
,
'
egon
'
,20,20,20
)
16
>>> r1.tell_area()
#
使用者调用接口tell_area
17
18
19
20
21
#
类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
22
class
Room:
23
def
__init__
(self,name,owner,width,length,high):
24
self.name=
name
25
self.owner=
owner
26
self.
__width
=
width
27
self.
__length
=
length
28
self.
__high
=
high
29
30
def
tell_area(self):
#
对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
31
return
self.
__width
* self.
__length
* self.
__high
32
33
34
#
对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
35
>>> r1.tell_area()
3.2.4 property属性
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
1
class
People:
2
def
__init__
(self,name,weight,height):
3
self.name=
name
4
self.weight=
weight
5
self.height=
height
6
7
@property
8
def
bmi(self):
9
return
self.weight / (self.height**2
)
10
11
p1=People(
'
egon
'
,75,1.85
)
12
print
(p1.bmi)
1
#
例二:圆的周长和面积
2
import
math
3
4
class
Circle:
5
def
__init__
(self,radius):
#
圆的半径radius
6
self.radius=
radius
7
8
@property
9
def
area(self):
10
return
math.pi * self.radius**2
#
计算面积
11
12
@property
13
def
perimeter(self):
14
return
2*math.pi*self.radius
#
计算周长
15
16
c=Circle(10
)
17
print
(c.radius)
18
print
(c.area)
#
可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
19
print
(c.perimeter)
#
同上
20
'''
21
输出结果:
22
314.1592653589793
23
62.83185307179586
24
'''
为什么要用property
1
class
Foo: # 方式一
2
@property
3
def
AAA(self):
4
print
(
'
get的时候运行我啊
'
)
5
6
7
@AAA.setter
8
def
AAA(self,value):
9
print
(
'
set的时候运行我啊
'
)
10
11
12
@AAA.deleter
13
def
AAA(self):
14
print
(
'
delete的时候运行我啊
'
)
15
16
17
#
只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
18
f1=
Foo()
19
f1.AAA
20
f1.AAA=
'
aaa
'
21
del
f1.AAA
1
#
方式二
2
class
Foo:
3
def
get_AAA(self):
4
print
(
'
get的时候运行我啊
'
)
5
6
7
def
set_AAA(self,value):
8
print
(
'
set的时候运行我啊
'
)
9
10
11
def
delete_AAA(self):
12
print
(
'
delete的时候运行我啊
'
)
13
AAA=property(get_AAA,set_AAA,delete_AAA)
#
内置property三个参数与get,set,delete一一对应
14
15
16
f1=
Foo()
17
f1.AAA
18
f1.AAA=
'
aaa
'
19
del
f1.AAA
1
class
Goods:
2
3
4
def
__init__
(self):
5
#
原价
6
self.original_price = 100
7
#
折扣
8
self.discount = 0.8
9
10
11
@property
12
def
price(self):
13
#
实际价格 = 原价 * 折扣
14
new_price = self.original_price *
self.discount
15
return
new_price
16
17
18
@price.setter
19
def
price(self, value):
20
self.original_price =
value
21
22
23
@price.deleter
24
def
price(self):
25
del
self.original_price
26
27
28
29
30
obj =
Goods()
31
obj.price
#
获取商品价格
32
obj.price = 200
#
修改商品原价
33
print
(obj.price)
34
del
obj.price
#
删除商品原价
3.2.5 classmethod
1
class
Classmethod_Demo():
2
role =
'
dog
'
3
4
5
@classmethod
6
def
func(cls):
7
print
(cls.role)
8
9
10
Classmethod_Demo.func()
3.2.6 staticmethod
1
class
Staticmethod_Demo():
2
role =
'
dog
'
3
4
5
@staticmethod
6
def
func():
7
print
(
"
当普通方法用
"
)
8
9
10
Staticmethod_Demo.func()
3.3 多态
在需要使用父类对象的地方,也可以使用子类对象, 这种情况就叫多态.
比如, 在函数中,我需要调用 某一个父类对象的方法, 那么我们也可以在这个地方调用子类对象的方法.
如何使用多态:
1.子类继承父类
2.子类重写父类中的方法
3. 通过对象调用这个方法
1
#
定义父类
2
class
Father:
3
def
cure(self):
4
print
(
"
父亲给病人治病...
"
)
5
6
7
#
定义子类继承父类
8
class
Son(Father):
9
#
重写父类中的方法
10
def
cure(self):
11
print
(
"
儿子给病人治病...
"
)
12
13
14
#
定义函数,在里面 调用 医生的cure函数
15
def
call_cure(doctor):
16
17
#
调用医生治病的方法
18
doctor.cure()
19
20
21
#
创建父类对象
22
father =
Father()
23
#
调用函数,把父类对象传递函数
24
call_cure(father)
25
26
27
#
创建子类对象
28
son =
Son()
29
#
调用函数,把子类对象传递函数
30
call_cure(son)
多态的好处:
给call_cure(doctor)函数传递哪个对象,在它里面就会调用哪个对象的cure()方法,也就是说在它里面既可以调用son对象的cure()方法,也能调用father对象的cure()方法,当然了也可以在它里面调用Father类其它子类对象的cure()方法,这样可以让call_cure(doctor)函数变得更加灵活,额外增加了它的功能,提高了它的扩展性.
鸭子类型
四、面向对象的软件开发
面向对象的软件工程包括下面几个部:
面向对象的常用术语
抽象/实现

